summaryrefslogtreecommitdiffstats
path: root/src/render/renderers
diff options
context:
space:
mode:
Diffstat (limited to 'src/render/renderers')
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp975
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h254
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp846
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h173
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp574
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp103
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h77
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h98
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp882
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h174
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp1188
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h176
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp1184
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h176
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp1242
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h173
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h192
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri26
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp1211
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h250
-rw-r--r--src/render/renderers/opengl/io/glbuffer.cpp169
-rw-r--r--src/render/renderers/opengl/io/glbuffer_p.h110
-rw-r--r--src/render/renderers/opengl/io/io.pri8
-rw-r--r--src/render/renderers/opengl/jobs/filtercompatibletechniquejob.cpp95
-rw-r--r--src/render/renderers/opengl/jobs/filtercompatibletechniquejob_p.h92
-rw-r--r--src/render/renderers/opengl/jobs/jobs.pri15
-rw-r--r--src/render/renderers/opengl/jobs/materialparametergathererjob.cpp132
-rw-r--r--src/render/renderers/opengl/jobs/materialparametergathererjob_p.h109
-rw-r--r--src/render/renderers/opengl/jobs/renderviewbuilderjob.cpp105
-rw-r--r--src/render/renderers/opengl/jobs/renderviewbuilderjob_p.h96
-rw-r--r--src/render/renderers/opengl/jobs/renderviewinitializerjob.cpp104
-rw-r--r--src/render/renderers/opengl/jobs/renderviewinitializerjob_p.h107
-rw-r--r--src/render/renderers/opengl/jobs/renderviewjobutils.cpp540
-rw-r--r--src/render/renderers/opengl/jobs/renderviewjobutils_p.h187
-rw-r--r--src/render/renderers/opengl/opengl.pri18
-rw-r--r--src/render/renderers/opengl/renderer/commandthread.cpp195
-rw-r--r--src/render/renderers/opengl/renderer/commandthread_p.h115
-rw-r--r--src/render/renderers/opengl/renderer/glcommands.cpp67
-rw-r--r--src/render/renderers/opengl/renderer/glcommands_p.h89
-rw-r--r--src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp160
-rw-r--r--src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h110
-rw-r--r--src/render/renderers/opengl/renderer/rendercommand.cpp76
-rw-r--r--src/render/renderers/opengl/renderer/rendercommand_p.h130
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp2067
-rw-r--r--src/render/renderers/opengl/renderer/renderer.pri27
-rw-r--r--src/render/renderers/opengl/renderer/renderer_p.h405
-rw-r--r--src/render/renderers/opengl/renderer/renderercache_p.h87
-rw-r--r--src/render/renderers/opengl/renderer/renderqueue.cpp133
-rw-r--r--src/render/renderers/opengl/renderer/renderqueue_p.h102
-rw-r--r--src/render/renderers/opengl/renderer/renderview.cpp1108
-rw-r--r--src/render/renderers/opengl/renderer/renderview_p.h392
-rw-r--r--src/render/renderers/opengl/renderer/renderviewbuilder.cpp672
-rw-r--r--src/render/renderers/opengl/renderer/renderviewbuilder_p.h149
-rw-r--r--src/render/renderers/opengl/renderer/shaderparameterpack.cpp100
-rw-r--r--src/render/renderers/opengl/renderer/shaderparameterpack_p.h145
-rw-r--r--src/render/renderers/opengl/renderer/shadervariables_p.h152
-rw-r--r--src/render/renderers/opengl/renderstates/renderstates.pri7
-rw-r--r--src/render/renderers/opengl/renderstates/renderstateset.cpp393
-rw-r--r--src/render/renderers/opengl/renderstates/renderstateset_p.h128
-rw-r--r--src/render/renderers/opengl/textures/gltexture.cpp468
-rw-r--r--src/render/renderers/opengl/textures/gltexture_p.h245
-rw-r--r--src/render/renderers/opengl/textures/gltexturemanager_p.h79
-rw-r--r--src/render/renderers/opengl/textures/renderbuffer.cpp112
-rw-r--r--src/render/renderers/opengl/textures/renderbuffer_p.h90
-rw-r--r--src/render/renderers/opengl/textures/textures.pri12
-rw-r--r--src/render/renderers/renderers.pri7
66 files changed, 19883 insertions, 0 deletions
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp
new file mode 100644
index 000000000..c2ec3db59
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp
@@ -0,0 +1,975 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "graphicscontext_p.h"
+
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/material_p.h>
+#include <Qt3DRender/private/gltexture_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+#include <Qt3DRender/private/attribute_p.h>
+#include <Qt3DRender/private/rendercommand_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <Qt3DRender/private/rendertarget_p.h>
+#include <Qt3DRender/private/graphicshelperinterface_p.h>
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/gltexturemanager_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/qbuffer_p.h>
+#include <Qt3DRender/private/renderbuffer_p.h>
+#include <QOpenGLShaderProgram>
+
+#if !defined(QT_OPENGL_ES_2)
+#include <QOpenGLFunctions_2_0>
+#include <QOpenGLFunctions_3_2_Core>
+#include <QOpenGLFunctions_3_3_Core>
+#include <QOpenGLFunctions_4_3_Core>
+#include <Qt3DRender/private/graphicshelpergl2_p.h>
+#include <Qt3DRender/private/graphicshelpergl3_2_p.h>
+#include <Qt3DRender/private/graphicshelpergl3_3_p.h>
+#include <Qt3DRender/private/graphicshelpergl4_p.h>
+#endif
+#include <Qt3DRender/private/graphicshelperes2_p.h>
+#include <Qt3DRender/private/graphicshelperes3_p.h>
+#include <Qt3DRender/private/graphicshelperes3_2_p.h>
+
+#include <QSurface>
+#include <QWindow>
+#include <QOpenGLTexture>
+#include <QOpenGLDebugLogger>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+namespace {
+
+QOpenGLShader::ShaderType shaderType(Qt3DRender::QShaderProgram::ShaderType type)
+{
+ switch (type) {
+ case Qt3DRender::QShaderProgram::Vertex: return QOpenGLShader::Vertex;
+ case Qt3DRender::QShaderProgram::TessellationControl: return QOpenGLShader::TessellationControl;
+ case Qt3DRender::QShaderProgram::TessellationEvaluation: return QOpenGLShader::TessellationEvaluation;
+ case Qt3DRender::QShaderProgram::Geometry: return QOpenGLShader::Geometry;
+ case Qt3DRender::QShaderProgram::Fragment: return QOpenGLShader::Fragment;
+ case Qt3DRender::QShaderProgram::Compute: return QOpenGLShader::Compute;
+ default: Q_UNREACHABLE();
+ }
+}
+
+} // anonymous namespace
+
+namespace Qt3DRender {
+namespace Render {
+
+namespace {
+
+void logOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage)
+{
+ qDebug() << "OpenGL debug message:" << debugMessage;
+}
+
+} // anonymous
+
+GraphicsContext::GraphicsContext()
+ : m_initialized(false)
+ , m_supportsVAO(false)
+ , m_maxTextureUnits(0)
+ , m_defaultFBO(0)
+ , m_gl(nullptr)
+ , m_glHelper(nullptr)
+ , m_shaderCache(nullptr)
+ , m_debugLogger(nullptr)
+{
+}
+
+GraphicsContext::~GraphicsContext()
+{
+}
+
+void GraphicsContext::setOpenGLContext(QOpenGLContext* ctx)
+{
+ Q_ASSERT(ctx);
+ m_gl = ctx;
+}
+
+void GraphicsContext::initialize()
+{
+ m_initialized = true;
+
+ Q_ASSERT(m_gl && m_shaderCache);
+
+ m_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &m_maxTextureUnits);
+ qCDebug(Backend) << "context supports" << m_maxTextureUnits << "texture units";
+
+ if (m_gl->format().majorVersion() >= 3) {
+ m_supportsVAO = true;
+ } else {
+ QSet<QByteArray> extensions = m_gl->extensions();
+ m_supportsVAO = extensions.contains(QByteArrayLiteral("GL_OES_vertex_array_object"))
+ || extensions.contains(QByteArrayLiteral("GL_ARB_vertex_array_object"))
+ || extensions.contains(QByteArrayLiteral("GL_APPLE_vertex_array_object"));
+ }
+
+ m_defaultFBO = m_gl->defaultFramebufferObject();
+ qCDebug(Backend) << "VAO support = " << m_supportsVAO;
+}
+
+void GraphicsContext::clearBackBuffer(QClearBuffers::BufferTypeFlags buffers)
+{
+ if (buffers != QClearBuffers::None) {
+ GLbitfield mask = 0;
+
+ if (buffers & QClearBuffers::ColorBuffer)
+ mask |= GL_COLOR_BUFFER_BIT;
+ if (buffers & QClearBuffers::DepthBuffer)
+ mask |= GL_DEPTH_BUFFER_BIT;
+ if (buffers & QClearBuffers::StencilBuffer)
+ mask |= GL_STENCIL_BUFFER_BIT;
+
+ m_gl->functions()->glClear(mask);
+ }
+}
+
+bool GraphicsContext::hasValidGLHelper() const
+{
+ return m_glHelper != nullptr;
+}
+
+bool GraphicsContext::isInitialized() const
+{
+ return m_initialized;
+}
+
+bool GraphicsContext::makeCurrent(QSurface *surface)
+{
+ Q_ASSERT(m_gl);
+ if (!m_gl->makeCurrent(surface)) {
+ qCWarning(Backend) << Q_FUNC_INFO << "makeCurrent failed";
+ return false;
+ }
+
+ // Set the correct GL Helper depending on the surface
+ // If no helper exists, create one
+
+ m_glHelper = m_glHelpers.value(surface);
+ if (!m_glHelper) {
+ m_glHelper = resolveHighestOpenGLFunctions();
+ m_glHelpers.insert(surface, m_glHelper);
+ }
+ return true;
+}
+
+void GraphicsContext::doneCurrent()
+{
+ Q_ASSERT(m_gl);
+ m_gl->doneCurrent();
+ m_glHelper = nullptr;
+}
+
+// Called by GL Command Thread
+QOpenGLShaderProgram *GraphicsContext::createShaderProgram(Shader *shaderNode)
+{
+ QScopedPointer<QOpenGLShaderProgram> shaderProgram(new QOpenGLShaderProgram);
+
+ // Compile shaders
+ const auto shaderCode = shaderNode->shaderCode();
+ QString logs;
+ for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) {
+ QShaderProgram::ShaderType type = static_cast<const QShaderProgram::ShaderType>(i);
+ if (!shaderCode.at(i).isEmpty()) {
+ // Note: logs only return the error but not all the shader code
+ // we could append it
+ if (!shaderProgram->addCacheableShaderFromSourceCode(shaderType(type), shaderCode.at(i)))
+ logs += shaderProgram->log();
+ }
+ }
+
+ // Call glBindFragDataLocation and link the program
+ // Since we are sharing shaders in the backend, we assume that if using custom
+ // fragOutputs, they should all be the same for a given shader
+ bindFragOutputs(shaderProgram->programId(), shaderNode->fragOutputs());
+
+ const bool linkSucceeded = shaderProgram->link();
+ logs += shaderProgram->log();
+ shaderNode->setLog(logs);
+ shaderNode->setStatus(linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error);
+
+ if (!linkSucceeded)
+ return nullptr;
+
+ // take from scoped-pointer so it doesn't get deleted
+ return shaderProgram.take();
+}
+
+// Called by GL Command Thread (can't use global glHelpers)
+// That assumes that the shaderProgram in Shader stays the same
+void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram)
+{
+ GraphicsHelperInterface *glHelper = resolveHighestOpenGLFunctions();
+ shader->initializeUniforms(glHelper->programUniformsAndLocations(shaderProgram->programId()));
+ shader->initializeAttributes(glHelper->programAttributesAndLocations(shaderProgram->programId()));
+ if (m_glHelper->supportsFeature(GraphicsHelperInterface::UniformBufferObject))
+ shader->initializeUniformBlocks(glHelper->programUniformBlocks(shaderProgram->programId()));
+ if (m_glHelper->supportsFeature(GraphicsHelperInterface::ShaderStorageObject))
+ shader->initializeShaderStorageBlocks(glHelper->programShaderStorageBlocks(shaderProgram->programId()));
+}
+
+
+// Called by GL Command Thread
+void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager)
+{
+ bool wasPresent = false;
+ QOpenGLShaderProgram *shaderProgram = m_shaderCache->getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent);
+ if (!shaderProgram && !wasPresent) {
+ // No matching QOpenGLShader in the cache so create one
+ shaderProgram = createShaderProgram(shader);
+
+ // Store in cache (even when failed and shaderProgram is null)
+ m_shaderCache->insert(shader->dna(), shader->peerId(), shaderProgram);
+ }
+
+ // Ensure the Shader node knows about the program interface
+ if (Q_LIKELY(shaderProgram != nullptr) && !shader->isLoaded()) {
+
+ // Find an already loaded shader that shares the same QOpenGLShaderProgram
+ Shader *refShader = nullptr;
+ const QVector<Qt3DCore::QNodeId> sharedShaderIds = m_shaderCache->shaderIdsForProgram(shader->dna());
+ for (const Qt3DCore::QNodeId sharedShaderId : sharedShaderIds) {
+ Shader *sharedShader = manager->lookupResource(sharedShaderId);
+ // Note: no need to check if shader->peerId != sharedShader->peerId
+ // as we are sure that this code path is executed only if !shared->isLoaded
+ if (sharedShader->isLoaded()) {
+ refShader = sharedShader;
+ break;
+ }
+ }
+
+ // We only introspect once per actual OpenGL shader program
+ // rather than once per ShaderNode.
+ if (refShader != nullptr)
+ shader->initializeFromReference(*refShader);
+ else // Introspect and set up interface description on Shader backend node
+ introspectShaderInterface(shader, shaderProgram);
+
+ shader->setGraphicsContext(this);
+ shader->setLoaded(true);
+ shader->markDirty(AbstractRenderer::AllDirty);
+ }
+}
+
+void GraphicsContext::removeShaderProgramReference(Shader *shaderNode)
+{
+ m_shaderCache->removeRef(shaderNode->dna(), shaderNode->peerId());
+}
+
+void GraphicsContext::activateDrawBuffers(const AttachmentPack &attachments)
+{
+ 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 {
+ qWarning() << "FBO incomplete";
+ }
+}
+
+/*!
+ * \internal
+ * Finds the highest supported opengl version and internally use the most optimized
+ * helper for a given version.
+ */
+GraphicsHelperInterface *GraphicsContext::resolveHighestOpenGLFunctions()
+{
+ Q_ASSERT(m_gl);
+ GraphicsHelperInterface *glHelper = nullptr;
+
+ if (m_gl->isOpenGLES()) {
+ if (m_gl->format().majorVersion() >= 3) {
+ if (m_gl->format().minorVersion() >= 2) {
+ glHelper = new GraphicsHelperES3_2;
+ qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL ES 3.2 Helper";
+ } else {
+ glHelper = new GraphicsHelperES3();
+ qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL ES 3.0 Helper";
+ }
+ } else {
+ glHelper = new GraphicsHelperES2();
+ qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL ES2 Helper";
+ }
+ glHelper->initializeHelper(m_gl, nullptr);
+ }
+#ifndef QT_OPENGL_ES_2
+ else {
+ QAbstractOpenGLFunctions *glFunctions = nullptr;
+ if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_4_3_Core>()) != nullptr) {
+ qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 4.3";
+ glHelper = new GraphicsHelperGL4();
+ } else if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_3_3_Core>()) != nullptr) {
+ qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 3.3";
+ glHelper = new GraphicsHelperGL3_3();
+ } else if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_3_2_Core>()) != nullptr) {
+ qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 3.2";
+ glHelper = new GraphicsHelperGL3_2();
+ } else if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_2_0>()) != nullptr) {
+ qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 2 Helper";
+ glHelper = new GraphicsHelperGL2();
+ }
+ Q_ASSERT_X(glHelper, "GraphicsContext::resolveHighestOpenGLFunctions", "unable to create valid helper for available OpenGL version");
+ glHelper->initializeHelper(m_gl, glFunctions);
+ }
+#endif
+
+ // Note: at this point we are certain the context (m_gl) is current with a surface
+ const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING");
+ const bool enableDebugLogging = !debugLoggingMode.isEmpty();
+
+ if (enableDebugLogging && !m_debugLogger) {
+ if (m_gl->hasExtension("GL_KHR_debug")) {
+ qCDebug(Backend) << "Qt3D: Enabling OpenGL debug logging";
+ m_debugLogger.reset(new QOpenGLDebugLogger);
+ if (m_debugLogger->initialize()) {
+ QObject::connect(m_debugLogger.data(), &QOpenGLDebugLogger::messageLogged, &logOpenGLDebugMessage);
+ const QString mode = QString::fromLocal8Bit(debugLoggingMode);
+ m_debugLogger->startLogging(mode.startsWith(QLatin1String("sync"), Qt::CaseInsensitive)
+ ? QOpenGLDebugLogger::SynchronousLogging
+ : QOpenGLDebugLogger::AsynchronousLogging);
+
+ const auto msgs = m_debugLogger->loggedMessages();
+ for (const QOpenGLDebugMessage &msg : msgs)
+ logOpenGLDebugMessage(msg);
+ }
+ } else {
+ qCDebug(Backend) << "Qt3D: OpenGL debug logging requested but GL_KHR_debug not supported";
+ }
+ }
+
+
+ // Set Vendor and Extensions of reference GraphicsApiFilter
+ // TO DO: would that vary like the glHelper ?
+
+ QStringList extensions;
+ const auto exts = m_gl->extensions();
+ for (const QByteArray &ext : exts)
+ extensions << QString::fromUtf8(ext);
+ m_contextInfo.m_major = m_gl->format().version().first;
+ m_contextInfo.m_minor = m_gl->format().version().second;
+ m_contextInfo.m_api = m_gl->isOpenGLES() ? QGraphicsApiFilter::OpenGLES : QGraphicsApiFilter::OpenGL;
+ m_contextInfo.m_profile = static_cast<QGraphicsApiFilter::OpenGLProfile>(m_gl->format().profile());
+ m_contextInfo.m_extensions = extensions;
+ m_contextInfo.m_vendor = QString::fromUtf8(reinterpret_cast<const char *>(m_gl->functions()->glGetString(GL_VENDOR)));
+
+ return glHelper;
+}
+
+const GraphicsApiFilterData *GraphicsContext::contextInfo() const
+{
+ return &m_contextInfo;
+}
+
+bool GraphicsContext::supportsDrawBuffersBlend() const
+{
+ return m_glHelper->supportsFeature(GraphicsHelperInterface::DrawBuffersBlend);
+}
+
+/*!
+ * \internal
+ * Wraps an OpenGL call to glDrawElementsInstanced.
+ * If the call is not supported by the system's OpenGL version,
+ * it is simulated with a loop.
+ */
+void GraphicsContext::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLsizei instances,
+ GLint baseVertex,
+ GLint baseInstance)
+{
+ m_glHelper->drawElementsInstancedBaseVertexBaseInstance(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ instances,
+ baseVertex,
+ baseInstance);
+}
+
+/*!
+ * \internal
+ * Wraps an OpenGL call to glDrawArraysInstanced.
+ */
+void GraphicsContext::drawArraysInstanced(GLenum primitiveType,
+ GLint first,
+ GLsizei count,
+ GLsizei instances)
+{
+ m_glHelper->drawArraysInstanced(primitiveType,
+ first,
+ count,
+ instances);
+}
+
+void GraphicsContext::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseinstance)
+{
+ m_glHelper->drawArraysInstancedBaseInstance(primitiveType,
+ first,
+ count,
+ instances,
+ baseinstance);
+}
+
+/*!
+ * \internal
+ * Wraps an OpenGL call to glDrawElements.
+ */
+void GraphicsContext::drawElements(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLint baseVertex)
+{
+ m_glHelper->drawElements(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ baseVertex);
+}
+
+void GraphicsContext::drawElementsIndirect(GLenum mode,
+ GLenum type,
+ void *indirect)
+{
+ m_glHelper->drawElementsIndirect(mode, type, indirect);
+}
+
+/*!
+ * \internal
+ * Wraps an OpenGL call to glDrawArrays.
+ */
+void GraphicsContext::drawArrays(GLenum primitiveType,
+ GLint first,
+ GLsizei count)
+{
+ m_glHelper->drawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsContext::drawArraysIndirect(GLenum mode, void *indirect)
+{
+ m_glHelper->drawArraysIndirect(mode, indirect);
+}
+
+void GraphicsContext::setVerticesPerPatch(GLint verticesPerPatch)
+{
+ m_glHelper->setVerticesPerPatch(verticesPerPatch);
+}
+
+void GraphicsContext::blendEquation(GLenum mode)
+{
+ m_glHelper->blendEquation(mode);
+}
+
+void GraphicsContext::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor)
+{
+ m_glHelper->blendFunci(buf, sfactor, dfactor);
+}
+
+void GraphicsContext::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha)
+{
+ m_glHelper->blendFuncSeparatei(buf, sRGB, dRGB, sAlpha, dAlpha);
+}
+
+void GraphicsContext::alphaTest(GLenum mode1, GLenum mode2)
+{
+ m_glHelper->alphaTest(mode1, mode2);
+}
+
+void GraphicsContext::bindFramebuffer(GLuint fbo, GraphicsHelperInterface::FBOBindMode mode)
+{
+ m_glHelper->bindFrameBufferObject(fbo, mode);
+}
+
+void GraphicsContext::depthTest(GLenum mode)
+{
+ m_glHelper->depthTest(mode);
+}
+
+void GraphicsContext::depthMask(GLenum mode)
+{
+ m_glHelper->depthMask(mode);
+}
+
+void GraphicsContext::frontFace(GLenum mode)
+{
+ m_glHelper->frontFace(mode);
+}
+
+void GraphicsContext::bindFragOutputs(GLuint shader, const QHash<QString, int> &outputs)
+{
+ if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT) &&
+ m_glHelper->supportsFeature(GraphicsHelperInterface::BindableFragmentOutputs))
+ m_glHelper->bindFragDataLocation(shader, outputs);
+}
+
+void GraphicsContext::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+ m_glHelper->bindUniformBlock(programId, uniformBlockIndex, uniformBlockBinding);
+}
+
+void GraphicsContext::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding)
+{
+ m_glHelper->bindShaderStorageBlock(programId, shaderStorageBlockIndex, shaderStorageBlockBinding);
+}
+
+void GraphicsContext::bindBufferBase(GLenum target, GLuint bindingIndex, GLuint buffer)
+{
+ m_glHelper->bindBufferBase(target, bindingIndex, buffer);
+}
+
+void GraphicsContext::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer)
+{
+ m_glHelper->buildUniformBuffer(v, description, buffer);
+}
+
+void GraphicsContext::setMSAAEnabled(bool enabled)
+{
+ m_glHelper->setMSAAEnabled(enabled);
+}
+
+void GraphicsContext::setAlphaCoverageEnabled(bool enabled)
+{
+ m_glHelper->setAlphaCoverageEnabled(enabled);
+}
+
+void GraphicsContext::clearBufferf(GLint drawbuffer, const QVector4D &values)
+{
+ m_glHelper->clearBufferf(drawbuffer, values);
+}
+
+GLuint GraphicsContext::boundFrameBufferObject()
+{
+ return m_glHelper->boundFrameBufferObject();
+}
+
+void GraphicsContext::clearColor(const QColor &color)
+{
+ m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
+}
+
+void GraphicsContext::clearDepthValue(float depth)
+{
+ m_gl->functions()->glClearDepthf(depth);
+}
+
+void GraphicsContext::clearStencilValue(int stencil)
+{
+ m_gl->functions()->glClearStencil(stencil);
+}
+
+void GraphicsContext::enableClipPlane(int clipPlane)
+{
+ m_glHelper->enableClipPlane(clipPlane);
+}
+
+void GraphicsContext::disableClipPlane(int clipPlane)
+{
+ m_glHelper->disableClipPlane(clipPlane);
+}
+
+void GraphicsContext::setClipPlane(int clipPlane, const QVector3D &normal, float distance)
+{
+ m_glHelper->setClipPlane(clipPlane, normal, distance);
+}
+
+GLint GraphicsContext::maxClipPlaneCount()
+{
+ return m_glHelper->maxClipPlaneCount();
+}
+
+GLint GraphicsContext::maxTextureUnitsCount()
+{
+ return m_maxTextureUnits;
+}
+
+void GraphicsContext::enablePrimitiveRestart(int restartIndex)
+{
+ if (m_glHelper->supportsFeature(GraphicsHelperInterface::PrimitiveRestart))
+ m_glHelper->enablePrimitiveRestart(restartIndex);
+}
+
+void GraphicsContext::disablePrimitiveRestart()
+{
+ if (m_glHelper->supportsFeature(GraphicsHelperInterface::PrimitiveRestart))
+ m_glHelper->disablePrimitiveRestart();
+}
+
+void GraphicsContext::pointSize(bool programmable, GLfloat value)
+{
+ m_glHelper->pointSize(programmable, value);
+}
+
+void GraphicsContext::dispatchCompute(int x, int y, int z)
+{
+ if (m_glHelper->supportsFeature(GraphicsHelperInterface::Compute))
+ m_glHelper->dispatchCompute(x, y, z);
+}
+
+GLboolean GraphicsContext::unmapBuffer(GLenum target)
+{
+ return m_glHelper->unmapBuffer(target);
+}
+
+char *GraphicsContext::mapBuffer(GLenum target, GLsizeiptr size)
+{
+ return m_glHelper->mapBuffer(target, size);
+}
+
+void GraphicsContext::enablei(GLenum cap, GLuint index)
+{
+ m_glHelper->enablei(cap, index);
+}
+
+void GraphicsContext::disablei(GLenum cap, GLuint index)
+{
+ m_glHelper->disablei(cap, index);
+}
+
+void GraphicsContext::setSeamlessCubemap(bool enable)
+{
+ m_glHelper->setSeamlessCubemap(enable);
+}
+
+void GraphicsContext::readBuffer(GLenum mode)
+{
+ m_glHelper->readBuffer(mode);
+}
+
+void GraphicsContext::drawBuffer(GLenum mode)
+{
+ m_glHelper->drawBuffer(mode);
+}
+
+void GraphicsContext::applyUniform(const ShaderUniform &description, const UniformValue &v)
+{
+ const UniformType type = m_glHelper->uniformTypeFromGLType(description.m_type);
+
+ switch (type) {
+ case UniformType::Float:
+ // See QTBUG-57510 and uniform_p.h
+ if (v.storedType() == Int) {
+ float value = float(*v.constData<int>());
+ UniformValue floatV(value);
+ applyUniformHelper<UniformType::Float>(description, floatV);
+ } else {
+ applyUniformHelper<UniformType::Float>(description, v);
+ }
+ break;
+ case UniformType::Vec2:
+ applyUniformHelper<UniformType::Vec2>(description, v);
+ break;
+ case UniformType::Vec3:
+ applyUniformHelper<UniformType::Vec3>(description, v);
+ break;
+ case UniformType::Vec4:
+ applyUniformHelper<UniformType::Vec4>(description, v);
+ break;
+
+ case UniformType::Double:
+ applyUniformHelper<UniformType::Double>(description, v);
+ break;
+ case UniformType::DVec2:
+ applyUniformHelper<UniformType::DVec2>(description, v);
+ break;
+ case UniformType::DVec3:
+ applyUniformHelper<UniformType::DVec3>(description, v);
+ break;
+ case UniformType::DVec4:
+ applyUniformHelper<UniformType::DVec4>(description, v);
+ break;
+
+ case UniformType::Sampler:
+ case UniformType::Int:
+ applyUniformHelper<UniformType::Int>(description, v);
+ break;
+ case UniformType::IVec2:
+ applyUniformHelper<UniformType::IVec2>(description, v);
+ break;
+ case UniformType::IVec3:
+ applyUniformHelper<UniformType::IVec3>(description, v);
+ break;
+ case UniformType::IVec4:
+ applyUniformHelper<UniformType::IVec4>(description, v);
+ break;
+
+ case UniformType::UInt:
+ applyUniformHelper<UniformType::UInt>(description, v);
+ break;
+ case UniformType::UIVec2:
+ applyUniformHelper<UniformType::UIVec2>(description, v);
+ break;
+ case UniformType::UIVec3:
+ applyUniformHelper<UniformType::UIVec3>(description, v);
+ break;
+ case UniformType::UIVec4:
+ applyUniformHelper<UniformType::UIVec4>(description, v);
+ break;
+
+ case UniformType::Bool:
+ applyUniformHelper<UniformType::Bool>(description, v);
+ break;
+ case UniformType::BVec2:
+ applyUniformHelper<UniformType::BVec2>(description, v);
+ break;
+ case UniformType::BVec3:
+ applyUniformHelper<UniformType::BVec3>(description, v);
+ break;
+ case UniformType::BVec4:
+ applyUniformHelper<UniformType::BVec4>(description, v);
+ break;
+
+ case UniformType::Mat2:
+ applyUniformHelper<UniformType::Mat2>(description, v);
+ break;
+ case UniformType::Mat3:
+ applyUniformHelper<UniformType::Mat3>(description, v);
+ break;
+ case UniformType::Mat4:
+ applyUniformHelper<UniformType::Mat4>(description, v);
+ break;
+ case UniformType::Mat2x3:
+ applyUniformHelper<UniformType::Mat2x3>(description, v);
+ break;
+ case UniformType::Mat3x2:
+ applyUniformHelper<UniformType::Mat3x2>(description, v);
+ break;
+ case UniformType::Mat2x4:
+ applyUniformHelper<UniformType::Mat2x4>(description, v);
+ break;
+ case UniformType::Mat4x2:
+ applyUniformHelper<UniformType::Mat4x2>(description, v);
+ break;
+ case UniformType::Mat3x4:
+ applyUniformHelper<UniformType::Mat3x4>(description, v);
+ break;
+ case UniformType::Mat4x3:
+ applyUniformHelper<UniformType::Mat4x3>(description, v);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GraphicsContext::memoryBarrier(QMemoryBarrier::Operations barriers)
+{
+ m_glHelper->memoryBarrier(barriers);
+}
+
+GLint GraphicsContext::elementType(GLint type)
+{
+ switch (type) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ return GL_FLOAT;
+
+#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2
+ case GL_DOUBLE:
+#ifdef GL_DOUBLE_VEC3 // For compiling on pre GL 4.1 systems
+ case GL_DOUBLE_VEC2:
+ case GL_DOUBLE_VEC3:
+ case GL_DOUBLE_VEC4:
+#endif
+ return GL_DOUBLE;
+#endif
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported:" << QString::number(type, 16);
+ }
+
+ return GL_INVALID_VALUE;
+}
+
+GLint GraphicsContext::tupleSizeFromType(GLint type)
+{
+ switch (type) {
+ case GL_FLOAT:
+#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2
+ case GL_DOUBLE:
+#endif
+ case GL_UNSIGNED_BYTE:
+ case GL_UNSIGNED_INT:
+ break; // fall through
+
+ case GL_FLOAT_VEC2:
+#ifdef GL_DOUBLE_VEC2 // For compiling on pre GL 4.1 systems.
+ case GL_DOUBLE_VEC2:
+#endif
+ return 2;
+
+ case GL_FLOAT_VEC3:
+#ifdef GL_DOUBLE_VEC3 // For compiling on pre GL 4.1 systems.
+ case GL_DOUBLE_VEC3:
+#endif
+ return 3;
+
+ case GL_FLOAT_VEC4:
+#ifdef GL_DOUBLE_VEC4 // For compiling on pre GL 4.1 systems.
+ case GL_DOUBLE_VEC4:
+#endif
+ return 4;
+
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported:" << QString::number(type, 16);
+ }
+
+ return 1;
+}
+
+GLuint GraphicsContext::byteSizeFromType(GLint type)
+{
+ switch (type) {
+ case GL_FLOAT: return sizeof(float);
+#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2
+ case GL_DOUBLE: return sizeof(double);
+#endif
+ case GL_UNSIGNED_BYTE: return sizeof(unsigned char);
+ case GL_UNSIGNED_INT: return sizeof(GLuint);
+
+ case GL_FLOAT_VEC2: return sizeof(float) * 2;
+ case GL_FLOAT_VEC3: return sizeof(float) * 3;
+ case GL_FLOAT_VEC4: return sizeof(float) * 4;
+#ifdef GL_DOUBLE_VEC3 // Required to compile on pre GL 4.1 systems
+ case GL_DOUBLE_VEC2: return sizeof(double) * 2;
+ case GL_DOUBLE_VEC3: return sizeof(double) * 3;
+ case GL_DOUBLE_VEC4: return sizeof(double) * 4;
+#endif
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported:" << QString::number(type, 16);
+ }
+
+ return 0;
+}
+
+GLint GraphicsContext::glDataTypeFromAttributeDataType(QAttribute::VertexBaseType dataType)
+{
+ switch (dataType) {
+ case QAttribute::Byte:
+ return GL_BYTE;
+ case QAttribute::UnsignedByte:
+ return GL_UNSIGNED_BYTE;
+ case QAttribute::Short:
+ return GL_SHORT;
+ case QAttribute::UnsignedShort:
+ return GL_UNSIGNED_SHORT;
+ case QAttribute::Int:
+ return GL_INT;
+ case QAttribute::UnsignedInt:
+ return GL_UNSIGNED_INT;
+ case QAttribute::HalfFloat:
+#ifdef GL_HALF_FLOAT
+ return GL_HALF_FLOAT;
+#endif
+#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2
+ case QAttribute::Double:
+ return GL_DOUBLE;
+#endif
+ case QAttribute::Float:
+ break;
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported dataType:" << dataType;
+ }
+ return GL_FLOAT;
+}
+
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Float, float, glUniform1fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec2, float, glUniform2fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec3, float, glUniform3fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec4, float, glUniform4fv)
+
+// OpenGL expects int* as values for booleans
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Bool, int, glUniform1iv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::BVec2, int, glUniform2iv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::BVec3, int, glUniform3iv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::BVec4, int, glUniform4iv)
+
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Int, int, glUniform1iv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::IVec2, int, glUniform2iv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::IVec3, int, glUniform3iv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::IVec4, int, glUniform4iv)
+
+QT3D_UNIFORM_TYPE_IMPL(UniformType::UInt, uint, glUniform1uiv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::UIVec2, uint, glUniform2uiv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::UIVec3, uint, glUniform3uiv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::UIVec4, uint, glUniform4uiv)
+
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat2, float, glUniformMatrix2fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat3, float, glUniformMatrix3fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat4, float, glUniformMatrix4fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat2x3, float, glUniformMatrix2x3fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat3x2, float, glUniformMatrix3x2fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat2x4, float, glUniformMatrix2x4fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat4x2, float, glUniformMatrix4x2fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat3x4, float, glUniformMatrix3x4fv)
+QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat4x3, float, glUniformMatrix4x3fv)
+
+} // namespace Render
+} // namespace Qt3DRender of namespace
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h
new file mode 100644
index 000000000..82db57433
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GRAPHICSCONTEXT_H
+#define QT3DRENDER_RENDER_GRAPHICSCONTEXT_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 <QOpenGLContext>
+#include <QOpenGLFunctions>
+#include <QOpenGLVertexArrayObject>
+#include <QHash>
+#include <QColor>
+#include <QMatrix4x4>
+#include <QBitArray>
+#include <QImage>
+#include <Qt3DRender/private/shaderparameterpack_p.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/glbuffer_p.h>
+#include <Qt3DRender/qattribute.h>
+#include <Qt3DRender/qmemorybarrier.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/qgraphicsapifilter_p.h>
+#include <Qt3DRender/private/shadercache_p.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <Qt3DRender/private/graphicshelperinterface_p.h>
+#include <Qt3DRender/private/qblitframebuffer_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLShaderProgram;
+class QAbstractOpenGLFunctions;
+class QOpenGLDebugLogger;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class GraphicsHelperInterface;
+class RenderTarget;
+class AttachmentPack;
+class ShaderManager;
+
+typedef QPair<QString, int> NamedUniformLocation;
+
+class Q_AUTOTEST_EXPORT GraphicsContext
+{
+public:
+ GraphicsContext();
+ ~GraphicsContext();
+
+ void setShaderCache(ShaderCache *shaderCache) { m_shaderCache = shaderCache; }
+ ShaderCache *shaderCache() const { return m_shaderCache; }
+
+ void setOpenGLContext(QOpenGLContext* ctx);
+ QOpenGLContext *openGLContext() { return m_gl; }
+ bool makeCurrent(QSurface *surface);
+ void doneCurrent();
+ bool hasValidGLHelper() const;
+ bool isInitialized() const;
+
+ // Shaders
+ QOpenGLShaderProgram *createShaderProgram(Shader *shaderNode);
+ void introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram);
+ void loadShader(Shader* shader, ShaderManager *manager);
+ void removeShaderProgramReference(Shader *shaderNode);
+
+ GLuint defaultFBO() const { return m_defaultFBO; }
+
+ const GraphicsApiFilterData *contextInfo() const;
+
+ // Wrapper methods
+ void clearBackBuffer(QClearBuffers::BufferTypeFlags buffers);
+ void alphaTest(GLenum mode1, GLenum mode2);
+ void bindFramebuffer(GLuint fbo, GraphicsHelperInterface::FBOBindMode mode);
+ void bindBufferBase(GLenum target, GLuint bindingIndex, GLuint buffer);
+ void bindFragOutputs(GLuint shader, const QHash<QString, int> &outputs);
+ void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
+ void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding);
+ void blendEquation(GLenum mode);
+ void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor);
+ void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha);
+ GLuint boundFrameBufferObject();
+ void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer);
+ void clearBufferf(GLint drawbuffer, const QVector4D &values);
+ void clearColor(const QColor &color);
+ void clearDepthValue(float depth);
+ void clearStencilValue(int stencil);
+ void depthMask(GLenum mode);
+ void depthTest(GLenum mode);
+ void disableClipPlane(int clipPlane);
+ void disablei(GLenum cap, GLuint index);
+ void disablePrimitiveRestart();
+ void dispatchCompute(int x, int y, int z);
+ char * mapBuffer(GLenum target, GLsizeiptr size);
+ GLboolean unmapBuffer(GLenum target);
+ void drawArrays(GLenum primitiveType, GLint first, GLsizei count);
+ void drawArraysIndirect(GLenum mode,void *indirect);
+ void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances);
+ void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseinstance);
+ void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLint baseVertex);
+ void drawElementsIndirect(GLenum mode, GLenum type, void *indirect);
+ void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLsizei instances, GLint baseVertex, GLint baseInstance);
+ void enableClipPlane(int clipPlane);
+ void enablei(GLenum cap, GLuint index);
+ void enablePrimitiveRestart(int restartIndex);
+ void frontFace(GLenum mode);
+ GLint maxClipPlaneCount();
+ GLint maxTextureUnitsCount();
+ void pointSize(bool programmable, GLfloat value);
+ void readBuffer(GLenum mode);
+ void drawBuffer(GLenum mode);
+ void setMSAAEnabled(bool enabled);
+ void setAlphaCoverageEnabled(bool enabled);
+ void setClipPlane(int clipPlane, const QVector3D &normal, float distance);
+ void setSeamlessCubemap(bool enable);
+ void setVerticesPerPatch(GLint verticesPerPatch);
+ void memoryBarrier(QMemoryBarrier::Operations barriers);
+ void activateDrawBuffers(const AttachmentPack &attachments);
+
+ // Helper methods
+ static GLint elementType(GLint type);
+ static GLint tupleSizeFromType(GLint type);
+ static GLuint byteSizeFromType(GLint type);
+ static GLint glDataTypeFromAttributeDataType(QAttribute::VertexBaseType dataType);
+
+ bool supportsDrawBuffersBlend() const;
+ bool supportsVAO() const { return m_supportsVAO; }
+
+ void initialize();
+ GraphicsHelperInterface *resolveHighestOpenGLFunctions();
+
+ bool m_initialized;
+ bool m_supportsVAO;
+ GLint m_maxTextureUnits;
+ GLuint m_defaultFBO;
+ QOpenGLContext *m_gl;
+ GraphicsHelperInterface *m_glHelper;
+
+ QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers;
+ GraphicsApiFilterData m_contextInfo;
+ ShaderCache *m_shaderCache;
+ QScopedPointer<QOpenGLDebugLogger> m_debugLogger;
+
+ friend class OpenGLVertexArrayObject;
+ OpenGLVertexArrayObject *m_currentVAO;
+
+ void applyUniform(const ShaderUniform &description, const UniformValue &v);
+
+ template<UniformType>
+ void applyUniformHelper(const ShaderUniform &, const UniformValue &) const
+ {
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Uniform: Didn't provide specialized apply() implementation");
+ }
+};
+
+#define QT3D_UNIFORM_TYPE_PROTO(UniformTypeEnum, BaseType, Func) \
+template<> \
+void GraphicsContext::applyUniformHelper<UniformTypeEnum>(const ShaderUniform &description, const UniformValue &value) const;
+
+#define QT3D_UNIFORM_TYPE_IMPL(UniformTypeEnum, BaseType, Func) \
+ template<> \
+ void GraphicsContext::applyUniformHelper<UniformTypeEnum>(const ShaderUniform &description, const UniformValue &value) const \
+{ \
+ const int count = qMin(description.m_size, int(value.byteSize() / description.m_rawByteSize)); \
+ m_glHelper->Func(description.m_location, count, value.constData<BaseType>()); \
+}
+
+
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Float, float, glUniform1fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Vec2, float, glUniform2fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Vec3, float, glUniform3fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Vec4, float, glUniform4fv)
+
+// OpenGL expects int* as values for booleans
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Bool, int, glUniform1iv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::BVec2, int, glUniform2iv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::BVec3, int, glUniform3iv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::BVec4, int, glUniform4iv)
+
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Int, int, glUniform1iv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::IVec2, int, glUniform2iv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::IVec3, int, glUniform3iv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::IVec4, int, glUniform4iv)
+
+QT3D_UNIFORM_TYPE_PROTO(UniformType::UInt, uint, glUniform1uiv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::UIVec2, uint, glUniform2uiv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::UIVec3, uint, glUniform3uiv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::UIVec4, uint, glUniform4uiv)
+
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat2, float, glUniformMatrix2fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat3, float, glUniformMatrix3fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat4, float, glUniformMatrix4fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat2x3, float, glUniformMatrix2x3fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat3x2, float, glUniformMatrix3x2fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat2x4, float, glUniformMatrix2x4fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat4x2, float, glUniformMatrix4x2fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat3x4, float, glUniformMatrix3x4fv)
+QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat4x3, float, glUniformMatrix4x3fv)
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GRAPHICSCONTEXT_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp
new file mode 100644
index 000000000..966528ad8
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp
@@ -0,0 +1,846 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "graphicshelperes2_p.h"
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <private/attachmentpack_p.h>
+#include <private/qgraphicsutils_p.h>
+#include <private/renderbuffer_p.h>
+#include <QtGui/private/qopenglextensions_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// ES 3.0+
+#ifndef GL_SAMPLER_3D
+#define GL_SAMPLER_3D 0x8B5F
+#endif
+#ifndef GL_SAMPLER_2D_SHADOW
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#endif
+#ifndef GL_SAMPLER_CUBE_SHADOW
+#define GL_SAMPLER_CUBE_SHADOW 0x8DC5
+#endif
+#ifndef GL_SAMPLER_2D_ARRAY
+#define GL_SAMPLER_2D_ARRAY 0x8DC1
+#endif
+#ifndef GL_SAMPLER_2D_ARRAY_SHADOW
+#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
+#endif
+
+// ES 2.0 FBO
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+
+namespace Qt3DRender {
+namespace Render {
+
+GraphicsHelperES2::GraphicsHelperES2()
+ : m_funcs(0)
+ , m_supportFramebufferBlit(false)
+{
+}
+
+GraphicsHelperES2::~GraphicsHelperES2()
+{
+}
+
+void GraphicsHelperES2::initializeHelper(QOpenGLContext *context,
+ QAbstractOpenGLFunctions *)
+{
+ Q_ASSERT(context);
+ m_funcs = context->functions();
+ Q_ASSERT(m_funcs);
+ m_ext.reset(new QOpenGLExtensions(context));
+ if (m_ext->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
+ m_supportFramebufferBlit = true;
+}
+
+void GraphicsHelperES2::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLsizei instances,
+ GLint baseVertex,
+ GLint baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2";
+
+ if (baseVertex != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertex is not supported with OpenGL ES 2";
+
+ for (GLint i = 0; i < instances; i++)
+ drawElements(primitiveType,
+ primitiveCount,
+ indexType,
+ indices);
+}
+
+void GraphicsHelperES2::drawArraysInstanced(GLenum primitiveType,
+ GLint first,
+ GLsizei count,
+ GLsizei instances)
+{
+ for (GLint i = 0; i < instances; i++)
+ drawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperES2::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL ES 2";
+ for (GLint i = 0; i < instances; i++)
+ drawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperES2::drawElements(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLint baseVertex)
+{
+ if (baseVertex != 0)
+ qWarning() << "glDrawElementsBaseVertex is not supported with OpenGL ES 2";
+ QOpenGLExtensions *xfuncs = static_cast<QOpenGLExtensions *>(m_funcs);
+ if (indexType == GL_UNSIGNED_INT && !xfuncs->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) {
+ static bool warnShown = false;
+ if (!warnShown) {
+ warnShown = true;
+ qWarning("GL_UNSIGNED_INT index type not supported on this system, skipping draw call.");
+ }
+ return;
+ }
+ m_funcs->glDrawElements(primitiveType,
+ primitiveCount,
+ indexType,
+ indices);
+}
+
+void GraphicsHelperES2::drawArrays(GLenum primitiveType,
+ GLint first,
+ GLsizei count)
+{
+ m_funcs->glDrawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperES2::drawElementsIndirect(GLenum, GLenum, void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL ES 2";
+}
+
+void GraphicsHelperES2::drawArraysIndirect(GLenum , void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL ES 2";
+}
+
+void GraphicsHelperES2::setVerticesPerPatch(GLint verticesPerPatch)
+{
+ Q_UNUSED(verticesPerPatch);
+ qWarning() << "Tessellation not supported with OpenGL ES 2";
+}
+
+void GraphicsHelperES2::useProgram(GLuint programId)
+{
+ m_funcs->glUseProgram(programId);
+}
+
+QVector<ShaderUniform> GraphicsHelperES2::programUniformsAndLocations(GLuint programId)
+{
+ QVector<ShaderUniform> uniforms;
+
+ GLint nbrActiveUniforms = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms);
+ uniforms.reserve(nbrActiveUniforms);
+ char uniformName[256];
+ for (GLint i = 0; i < nbrActiveUniforms; i++) {
+ ShaderUniform uniform;
+ GLsizei uniformNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength,
+ &uniform.m_size, &uniform.m_type, uniformName);
+ uniformName[sizeof(uniformName) - 1] = '\0';
+ uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName);
+ uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength);
+ // Work around for uniform array names that aren't returned with [0] by some drivers
+ if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]")))
+ uniform.m_name.append(QLatin1String("[0]"));
+ uniform.m_rawByteSize = uniformByteSize(uniform);
+ uniforms.append(uniform);
+ }
+ return uniforms;
+}
+
+QVector<ShaderAttribute> GraphicsHelperES2::programAttributesAndLocations(GLuint programId)
+{
+ QVector<ShaderAttribute> attributes;
+ GLint nbrActiveAttributes = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes);
+ attributes.reserve(nbrActiveAttributes);
+ char attributeName[256];
+ for (GLint i = 0; i < nbrActiveAttributes; i++) {
+ ShaderAttribute attribute;
+ GLsizei attributeNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength,
+ &attribute.m_size, &attribute.m_type, attributeName);
+ attributeName[sizeof(attributeName) - 1] = '\0';
+ attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName);
+ attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength);
+ attributes.append(attribute);
+ }
+ return attributes;
+}
+
+QVector<ShaderUniformBlock> GraphicsHelperES2::programUniformBlocks(GLuint programId)
+{
+ Q_UNUSED(programId);
+ QVector<ShaderUniformBlock> blocks;
+ qWarning() << "UBO are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)";
+ return blocks;
+}
+
+QVector<ShaderStorageBlock> GraphicsHelperES2::programShaderStorageBlocks(GLuint programId)
+{
+ Q_UNUSED(programId);
+ QVector<ShaderStorageBlock> blocks;
+ qWarning() << "SSBO are not supported by OpenGL ES 2.0 (since OpenGL ES 3.1)";
+ return blocks;
+}
+
+void GraphicsHelperES2::vertexAttribDivisor(GLuint index, GLuint divisor)
+{
+ Q_UNUSED(index);
+ Q_UNUSED(divisor);
+}
+
+void GraphicsHelperES2::vertexAttributePointer(GLenum shaderDataType,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer)
+{
+ switch (shaderDataType) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT4:
+ m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ break;
+
+ default:
+ qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type";
+ Q_UNREACHABLE();
+ }
+}
+
+void GraphicsHelperES2::readBuffer(GLenum mode)
+{
+ Q_UNUSED(mode)
+ qWarning() << "glReadBuffer not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)";
+}
+
+void GraphicsHelperES2::drawBuffer(GLenum mode)
+{
+ Q_UNUSED(mode);
+ qWarning() << "glDrawBuffer is not supported with OpenGL ES 2";
+}
+
+void GraphicsHelperES2::blendEquation(GLenum mode)
+{
+ m_funcs->glBlendEquation(mode);
+}
+
+void GraphicsHelperES2::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sfactor);
+ Q_UNUSED(dfactor);
+
+ qWarning() << "glBlendFunci() not supported by OpenGL ES 2.0";
+}
+
+void GraphicsHelperES2::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sRGB);
+ Q_UNUSED(dRGB);
+ Q_UNUSED(sAlpha);
+ Q_UNUSED(dAlpha);
+
+ qWarning() << "glBlendFuncSeparatei() not supported by OpenGL ES 2.0";
+}
+
+void GraphicsHelperES2::alphaTest(GLenum, GLenum)
+{
+ qCWarning(Render::Rendering) << Q_FUNC_INFO << "AlphaTest not available with OpenGL ES 2.0";
+}
+
+void GraphicsHelperES2::depthTest(GLenum mode)
+{
+ m_funcs->glEnable(GL_DEPTH_TEST);
+ m_funcs->glDepthFunc(mode);
+}
+
+void GraphicsHelperES2::depthMask(GLenum mode)
+{
+ m_funcs->glDepthMask(mode);
+}
+
+void GraphicsHelperES2::frontFace(GLenum mode)
+{
+ m_funcs->glFrontFace(mode);
+}
+
+void GraphicsHelperES2::setMSAAEnabled(bool enabled)
+{
+ Q_UNUSED(enabled);
+ qWarning() << "MSAA not available with OpenGL ES 2.0";
+}
+
+void GraphicsHelperES2::setAlphaCoverageEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)
+ : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+}
+
+GLuint GraphicsHelperES2::createFrameBufferObject()
+{
+ GLuint id;
+ m_funcs->glGenFramebuffers(1, &id);
+ return id;
+}
+
+void GraphicsHelperES2::releaseFrameBufferObject(GLuint frameBufferId)
+{
+ m_funcs->glDeleteFramebuffers(1, &frameBufferId);
+}
+
+void GraphicsHelperES2::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode)
+{
+ switch (mode) {
+ case FBODraw:
+ m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBORead:
+ m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBOReadAndDraw:
+ default:
+ m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId);
+ return;
+ }
+}
+
+GLuint GraphicsHelperES2::boundFrameBufferObject()
+{
+ GLint id = 0;
+ m_funcs->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &id);
+ return id;
+}
+
+bool GraphicsHelperES2::checkFrameBufferComplete()
+{
+ return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+}
+
+bool GraphicsHelperES2::frameBufferNeedsRenderBuffer(const Attachment &attachment)
+{
+ // Use a renderbuffer for combined depth+stencil attachments since this is
+ // problematic before GLES 3.2. Keep using textures for everything else.
+ return attachment.m_point == QRenderTargetOutput::DepthStencil;
+}
+
+void GraphicsHelperES2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment)
+{
+ GLenum attr = GL_COLOR_ATTACHMENT0;
+
+ if (attachment.m_point == QRenderTargetOutput::Color0)
+ attr = GL_COLOR_ATTACHMENT0;
+ else if (attachment.m_point == QRenderTargetOutput::Depth)
+ attr = GL_DEPTH_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::Stencil)
+ attr = GL_STENCIL_ATTACHMENT;
+ else
+ qCritical() << "Unsupported FBO attachment OpenGL ES 2.0";
+
+ const QOpenGLTexture::Target target = texture->target();
+
+ if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face == QAbstractTexture::AllFaces) {
+ qWarning() << "OpenGL ES 2.0 doesn't handle attaching all the faces of a cube map texture at once to an FBO";
+ return;
+ }
+
+ texture->bind();
+ if (target == QOpenGLTexture::Target2D)
+ m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel);
+ else if (target == QOpenGLTexture::TargetCubeMap)
+ m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel);
+ else
+ qCritical() << "Unsupported Texture FBO attachment format";
+ texture->release();
+}
+
+void GraphicsHelperES2::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment)
+{
+ if (attachment.m_point != QRenderTargetOutput::DepthStencil) {
+ qCritical() << "Renderbuffers only supported for combined depth-stencil, but got attachment point"
+ << attachment.m_point;
+ return;
+ }
+
+ renderBuffer->bind();
+ m_funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->renderBufferId());
+ m_funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->renderBufferId());
+ renderBuffer->release();
+}
+
+bool GraphicsHelperES2::supportsFeature(GraphicsHelperInterface::Feature feature) const
+{
+ switch (feature) {
+ case RenderBufferDimensionRetrieval:
+ return true;
+ case BlitFramebuffer:
+ return m_supportFramebufferBlit;
+ default:
+ return false;
+ }
+}
+void GraphicsHelperES2::drawBuffers(GLsizei, const int *)
+{
+ qWarning() << "drawBuffers is not supported by ES 2.0";
+}
+
+void GraphicsHelperES2::bindFragDataLocation(GLuint , const QHash<QString, int> &)
+{
+ qCritical() << "bindFragDataLocation is not supported by ES 2.0";
+}
+
+void GraphicsHelperES2::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+ Q_UNUSED(programId);
+ Q_UNUSED(uniformBlockIndex);
+ Q_UNUSED(uniformBlockBinding);
+ qWarning() << "UBO are not supported by ES 2.0 (since ES 3.0)";
+}
+
+void GraphicsHelperES2::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding)
+{
+ Q_UNUSED(programId);
+ Q_UNUSED(shaderStorageBlockIndex);
+ Q_UNUSED(shaderStorageBlockBinding);
+ qWarning() << "SSBO are not supported by ES 2.0 (since ES 3.1)";
+}
+
+void GraphicsHelperES2::bindBufferBase(GLenum target, GLuint index, GLuint buffer)
+{
+ Q_UNUSED(target);
+ Q_UNUSED(index);
+ Q_UNUSED(buffer);
+ qWarning() << "bindBufferBase is not supported by ES 2.0 (since ES 3.0)";
+}
+
+void GraphicsHelperES2::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer)
+{
+ Q_UNUSED(v);
+ Q_UNUSED(description);
+ Q_UNUSED(buffer);
+ qWarning() << "UBO are not supported by ES 2.0 (since ES 3.0)";
+}
+
+uint GraphicsHelperES2::uniformByteSize(const ShaderUniform &description)
+{
+ uint rawByteSize = 0;
+ int arrayStride = qMax(description.m_arrayStride, 0);
+ int matrixStride = qMax(description.m_matrixStride, 0);
+
+ switch (description.m_type) {
+
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ rawByteSize = 8;
+ break;
+
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ rawByteSize = 12;
+ break;
+
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ rawByteSize = 16;
+ break;
+
+ case GL_FLOAT_MAT2:
+ rawByteSize = matrixStride ? 2 * matrixStride : 16;
+ break;
+
+ case GL_FLOAT_MAT3:
+ rawByteSize = matrixStride ? 3 * matrixStride : 36;
+ break;
+
+ case GL_FLOAT_MAT4:
+ rawByteSize = matrixStride ? 4 * matrixStride : 64;
+ break;
+
+ case GL_BOOL:
+ rawByteSize = 1;
+ break;
+
+ case GL_BOOL_VEC2:
+ rawByteSize = 2;
+ break;
+
+ case GL_BOOL_VEC3:
+ rawByteSize = 3;
+ break;
+
+ case GL_BOOL_VEC4:
+ rawByteSize = 4;
+ break;
+
+ case GL_INT:
+ case GL_FLOAT:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_CUBE:
+ rawByteSize = 4;
+ break;
+ }
+
+ return arrayStride ? rawByteSize * arrayStride : rawByteSize;
+}
+
+void GraphicsHelperES2::enableClipPlane(int)
+{
+}
+
+void GraphicsHelperES2::disableClipPlane(int)
+{
+}
+
+void GraphicsHelperES2::setClipPlane(int, const QVector3D &, float)
+{
+ qWarning() << "Clip planes not supported by OpenGL ES 2.0";
+}
+
+GLint GraphicsHelperES2::maxClipPlaneCount()
+{
+ return 0;
+}
+
+void GraphicsHelperES2::memoryBarrier(QMemoryBarrier::Operations barriers)
+{
+ Q_UNUSED(barriers);
+ qWarning() << "memory barrier is not supported by OpenGL ES 2.0 (since 4.3)";
+}
+
+void GraphicsHelperES2::enablePrimitiveRestart(int)
+{
+}
+
+void GraphicsHelperES2::enableVertexAttributeArray(int location)
+{
+ m_funcs->glEnableVertexAttribArray(location);
+}
+
+void GraphicsHelperES2::disablePrimitiveRestart()
+{
+}
+
+void GraphicsHelperES2::clearBufferf(GLint drawbuffer, const QVector4D &values)
+{
+ Q_UNUSED(drawbuffer);
+ Q_UNUSED(values);
+ qWarning() << "glClearBuffer*() not supported by OpenGL ES 2.0";
+}
+
+void GraphicsHelperES2::pointSize(bool programmable, GLfloat value)
+{
+ // If this is not a reset to default values, print a warning
+ if (programmable || !qFuzzyCompare(value, 1.0f)) {
+ static bool warned = false;
+ if (!warned) {
+ qWarning() << "glPointSize() and GL_PROGRAM_POINT_SIZE are not supported by ES 2.0";
+ warned = true;
+ }
+ }
+}
+
+void GraphicsHelperES2::enablei(GLenum cap, GLuint index)
+{
+ Q_UNUSED(cap);
+ Q_UNUSED(index);
+ qWarning() << "glEnablei() not supported by OpenGL ES 2.0";
+}
+
+void GraphicsHelperES2::disablei(GLenum cap, GLuint index)
+{
+ Q_UNUSED(cap);
+ Q_UNUSED(index);
+ qWarning() << "glDisablei() not supported by OpenGL ES 2.0";
+}
+
+void GraphicsHelperES2::setSeamlessCubemap(bool enable)
+{
+ Q_UNUSED(enable);
+ qWarning() << "GL_TEXTURE_CUBE_MAP_SEAMLESS not supported by OpenGL ES 2.0";
+}
+
+QSize GraphicsHelperES2::getRenderBufferDimensions(GLuint renderBufferId)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+ return QSize(width, height);
+}
+
+QSize GraphicsHelperES2::getTextureDimensions(GLuint textureId, GLenum target, uint level)
+{
+ Q_UNUSED(textureId);
+ Q_UNUSED(target);
+ Q_UNUSED(level);
+ qCritical() << "getTextureDimensions is not supported by ES 2.0";
+ return QSize(0, 0);
+}
+
+void GraphicsHelperES2::dispatchCompute(GLuint wx, GLuint wy, GLuint wz)
+{
+ Q_UNUSED(wx);
+ Q_UNUSED(wy);
+ Q_UNUSED(wz);
+ qWarning() << "Compute Shaders are not supported by ES 2.0 (since ES 3.1)";
+}
+
+char *GraphicsHelperES2::mapBuffer(GLenum target, GLsizeiptr size)
+{
+ Q_UNUSED(target);
+ Q_UNUSED(size);
+ qWarning() << "Map buffer is not a core requirement for ES 2.0";
+ return nullptr;
+}
+
+GLboolean GraphicsHelperES2::unmapBuffer(GLenum target)
+{
+ Q_UNUSED(target);
+ qWarning() << "unMap buffer is not a core requirement for ES 2.0";
+ return false;
+}
+
+void GraphicsHelperES2::glUniform1fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform1fv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform2fv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform3fv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform4fv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform1iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform1iv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform2iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform2iv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform3iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform3iv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform4iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform4iv(location, count, values);
+}
+
+void GraphicsHelperES2::glUniform1uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform1uiv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniform2uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform2uiv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniform3uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform3uiv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniform4uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform4uiv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2fv(location, count, false, values);
+}
+
+void GraphicsHelperES2::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3fv(location, count, false, values);
+}
+
+void GraphicsHelperES2::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4fv(location, count, false, values);
+}
+
+void GraphicsHelperES2::glUniformMatrix2x3fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix2x3fv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniformMatrix3x2fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix3x2fv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniformMatrix2x4fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix2x4fv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniformMatrix4x2fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix4x2fv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniformMatrix3x4fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix3x4fv not supported by ES 2";
+}
+
+void GraphicsHelperES2::glUniformMatrix4x3fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix4x3fv not supported by ES 2";
+}
+
+UniformType GraphicsHelperES2::uniformTypeFromGLType(GLenum type)
+{
+ switch (type) {
+ case GL_FLOAT:
+ return UniformType::Float;
+ case GL_FLOAT_VEC2:
+ return UniformType::Vec2;
+ case GL_FLOAT_VEC3:
+ return UniformType::Vec3;
+ case GL_FLOAT_VEC4:
+ return UniformType::Vec4;
+ case GL_FLOAT_MAT2:
+ return UniformType::Mat2;
+ case GL_FLOAT_MAT3:
+ return UniformType::Mat3;
+ case GL_FLOAT_MAT4:
+ return UniformType::Mat4;
+ case GL_INT:
+ return UniformType::Int;
+ case GL_INT_VEC2:
+ return UniformType::IVec2;
+ case GL_INT_VEC3:
+ return UniformType::IVec3;
+ case GL_INT_VEC4:
+ return UniformType::IVec4;
+ case GL_BOOL:
+ return UniformType::Bool;
+ case GL_BOOL_VEC2:
+ return UniformType::BVec2;
+ case GL_BOOL_VEC3:
+ return UniformType::BVec3;
+ case GL_BOOL_VEC4:
+ return UniformType::BVec4;
+
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_CUBE:
+ return UniformType::Sampler;
+ default:
+ Q_UNREACHABLE();
+ return UniformType::Float;
+ }
+}
+
+void GraphicsHelperES2::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+ if (!m_supportFramebufferBlit)
+ qWarning() << "Framebuffer blits are not supported by ES 2.0 (since ES 3.1)";
+ else
+ m_ext->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h
new file mode 100644
index 000000000..1c6df41b6
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERES2_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERES2_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/graphicshelperinterface_p.h>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLExtensions;
+
+namespace Qt3DRender {
+namespace Render {
+
+class GraphicsHelperES2 : public GraphicsHelperInterface
+{
+public:
+ GraphicsHelperES2();
+ virtual ~GraphicsHelperES2();
+
+ // QGraphicHelperInterface interface
+ void alphaTest(GLenum mode1, GLenum mode2) override;
+ void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+ void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override;
+ bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override;
+ void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override;
+ void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override;
+ void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override;
+ void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override;
+ void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+ void blendEquation(GLenum mode) override;
+ void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override;
+ void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override;
+ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override;
+ GLuint boundFrameBufferObject() override;
+ void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override;
+ bool checkFrameBufferComplete() override;
+ void clearBufferf(GLint drawbuffer, const QVector4D &values) override;
+ GLuint createFrameBufferObject() override;
+ void depthMask(GLenum mode) override;
+ void depthTest(GLenum mode) override;
+ void disableClipPlane(int clipPlane) override;
+ void disablei(GLenum cap, GLuint index) override;
+ void disablePrimitiveRestart() override;
+ void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override;
+ char *mapBuffer(GLenum target, GLsizeiptr size) override;
+ GLboolean unmapBuffer(GLenum target) override;
+ void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override;
+ void drawArraysIndirect(GLenum mode,void *indirect) override;
+ void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override;
+ void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override;
+ void drawBuffers(GLsizei n, const int *bufs) override;
+ void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override;
+ void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override;
+ void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override;
+ void enableClipPlane(int clipPlane) override;
+ void enablei(GLenum cap, GLuint index) override;
+ void enablePrimitiveRestart(int primitiveRestartIndex) override;
+ void enableVertexAttributeArray(int location) override;
+ void frontFace(GLenum mode) override;
+ QSize getRenderBufferDimensions(GLuint renderBufferId) override;
+ QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override;
+ void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override;
+ void pointSize(bool programmable, GLfloat value) override;
+ GLint maxClipPlaneCount() override;
+ void memoryBarrier(QMemoryBarrier::Operations barriers) override;
+ QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override;
+ QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override;
+ QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override;
+ QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override;
+ void releaseFrameBufferObject(GLuint frameBufferId) override;
+ void setMSAAEnabled(bool enable) override;
+ void setAlphaCoverageEnabled(bool enable) override;
+ void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override;
+ void setSeamlessCubemap(bool enable) override;
+ void setVerticesPerPatch(GLint verticesPerPatch) override;
+ bool supportsFeature(Feature feature) const override;
+ uint uniformByteSize(const ShaderUniform &description) override;
+ void useProgram(GLuint programId) override;
+ void vertexAttribDivisor(GLuint index, GLuint divisor) override;
+ void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override;
+ void readBuffer(GLenum mode) override;
+ void drawBuffer(GLenum mode) override;
+
+ void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ void glUniform1iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform2iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform3iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform4iv(GLint location, GLsizei count, const GLint *value) override;
+
+ void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override;
+
+ void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ UniformType uniformTypeFromGLType(GLenum glType) override;
+
+protected:
+ QOpenGLFunctions *m_funcs;
+ bool m_supportFramebufferBlit;
+ QScopedPointer<QOpenGLExtensions> m_ext;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES2_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp
new file mode 100644
index 000000000..813c627b8
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp
@@ -0,0 +1,574 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Svenn-Arne Dragly.
+** 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 "graphicshelperes3_p.h"
+#include <private/attachmentpack_p.h>
+#include <private/qgraphicsutils_p.h>
+#include <private/renderlogging_p.h>
+#include <QOpenGLExtraFunctions>
+
+QT_BEGIN_NAMESPACE
+
+// ES 3.0+
+#ifndef GL_SAMPLER_3D
+#define GL_SAMPLER_3D 0x8B5F
+#endif
+#ifndef GL_SAMPLER_2D_SHADOW
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#endif
+#ifndef GL_SAMPLER_CUBE_SHADOW
+#define GL_SAMPLER_CUBE_SHADOW 0x8DC5
+#endif
+#ifndef GL_SAMPLER_2D_ARRAY
+#define GL_SAMPLER_2D_ARRAY 0x8DC1
+#endif
+#ifndef GL_SAMPLER_2D_ARRAY_SHADOW
+#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
+#endif
+#ifndef GL_FLOAT_MAT2x3
+#define GL_FLOAT_MAT2x3 0x8B65
+#endif
+#ifndef GL_FLOAT_MAT2x4
+#define GL_FLOAT_MAT2x4 0x8B66
+#endif
+#ifndef GL_FLOAT_MAT3x2
+#define GL_FLOAT_MAT3x2 0x8B67
+#endif
+#ifndef GL_FLOAT_MAT3x4
+#define GL_FLOAT_MAT3x4 0x8B68
+#endif
+#ifndef GL_FLOAT_MAT4x2
+#define GL_FLOAT_MAT4x2 0x8B69
+#endif
+#ifndef GL_FLOAT_MAT4x3
+#define GL_FLOAT_MAT4x3 0x8B6A
+#endif
+#ifndef GL_UNSIGNED_INT_VEC2
+#define GL_UNSIGNED_INT_VEC2 0x8DC6
+#endif
+#ifndef GL_UNSIGNED_INT_VEC3
+#define GL_UNSIGNED_INT_VEC3 0x8DC7
+#endif
+#ifndef GL_UNSIGNED_INT_VEC4
+#define GL_UNSIGNED_INT_VEC4 0x8DC8
+#endif
+#ifndef GL_INT_SAMPLER_2D
+#define GL_INT_SAMPLER_2D 0x8DCA
+#endif
+#ifndef GL_INT_SAMPLER_3D
+#define GL_INT_SAMPLER_3D 0x8DCB
+#endif
+#ifndef GL_INT_SAMPLER_CUBE
+#define GL_INT_SAMPLER_CUBE 0x8DCC
+#endif
+#ifndef GL_INT_SAMPLER_2D_ARRAY
+#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF
+#endif
+#ifndef GL_UNSIGNED_INT_SAMPLER_2D
+#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2
+#endif
+#ifndef GL_UNSIGNED_INT_SAMPLER_3D
+#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3
+#endif
+#ifndef GL_UNSIGNED_INT_SAMPLER_CUBE
+#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4
+#endif
+#ifndef GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
+#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7
+#endif
+
+#ifndef GL_ACTIVE_UNIFORM_BLOCKS
+#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
+#endif
+#ifndef GL_UNIFORM_BLOCK_INDEX
+#define GL_UNIFORM_BLOCK_INDEX 0x8A3A
+#endif
+#ifndef GL_UNIFORM_OFFSET
+#define GL_UNIFORM_OFFSET 0x8A3B
+#endif
+#ifndef GL_UNIFORM_ARRAY_STRIDE
+#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+#endif
+#ifndef GL_UNIFORM_MATRIX_STRIDE
+#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+#endif
+#ifndef GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS
+#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+#endif
+#ifndef GL_UNIFORM_BLOCK_BINDING
+#define GL_UNIFORM_BLOCK_BINDING 0x8A3F
+#endif
+#ifndef GL_UNIFORM_BLOCK_DATA_SIZE
+#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+#endif
+
+namespace Qt3DRender {
+namespace Render {
+
+GraphicsHelperES3::GraphicsHelperES3()
+{
+}
+
+GraphicsHelperES3::~GraphicsHelperES3()
+{
+}
+
+void GraphicsHelperES3::initializeHelper(QOpenGLContext *context,
+ QAbstractOpenGLFunctions *functions)
+{
+ GraphicsHelperES2::initializeHelper(context, functions);
+ m_extraFuncs = context->extraFunctions();
+ Q_ASSERT(m_extraFuncs);
+}
+
+void GraphicsHelperES3::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLsizei instances,
+ GLint baseVertex,
+ GLint baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 3";
+
+ if (baseVertex != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertex is not supported with OpenGL ES 3";
+
+ m_extraFuncs->glDrawElementsInstanced(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ instances);
+}
+
+void GraphicsHelperES3::vertexAttribDivisor(GLuint index, GLuint divisor)
+{
+ m_extraFuncs->glVertexAttribDivisor(index, divisor);
+}
+
+void GraphicsHelperES3::vertexAttributePointer(GLenum shaderDataType,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer)
+{
+ switch (shaderDataType) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_MAT4:
+ m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ break;
+
+ case GL_INT:
+ case GL_INT_VEC2:
+ case GL_INT_VEC3:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC4:
+ m_extraFuncs->glVertexAttribIPointer(index, size, type, stride, pointer);
+ break;
+
+ default:
+ qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type";
+ Q_UNREACHABLE();
+ }
+}
+
+void GraphicsHelperES3::readBuffer(GLenum mode)
+{
+ m_extraFuncs->glReadBuffer(mode);
+}
+
+void GraphicsHelperES3::drawBuffer(GLenum mode)
+{
+ Q_UNUSED(mode);
+ qWarning() << "glDrawBuffer is not supported with OpenGL ES 3";
+}
+
+void GraphicsHelperES3::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment)
+{
+ GLenum attr = GL_COLOR_ATTACHMENT0;
+
+ if (attachment.m_point <= QRenderTargetOutput::Color15)
+ attr = GL_COLOR_ATTACHMENT0 + attachment.m_point;
+ else if (attachment.m_point == QRenderTargetOutput::Depth)
+ attr = GL_DEPTH_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::Stencil)
+ attr = GL_STENCIL_ATTACHMENT;
+ else
+ qCritical() << "Unsupported FBO attachment OpenGL ES 3.0";
+
+ const QOpenGLTexture::Target target = texture->target();
+
+ if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face == QAbstractTexture::AllFaces) {
+ qWarning() << "OpenGL ES 3.0 doesn't handle attaching all the faces of a cube map texture at once to an FBO";
+ return;
+ }
+
+ texture->bind();
+ if (target == QOpenGLTexture::Target2D)
+ m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel);
+ else if (target == QOpenGLTexture::TargetCubeMap)
+ m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel);
+ else
+ qCritical() << "Unsupported Texture FBO attachment format";
+ texture->release();
+}
+
+bool GraphicsHelperES3::supportsFeature(GraphicsHelperInterface::Feature feature) const
+{
+ switch (feature) {
+ case RenderBufferDimensionRetrieval:
+ case MRT:
+ case BlitFramebuffer:
+ case UniformBufferObject:
+ case MapBuffer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void GraphicsHelperES3::drawBuffers(GLsizei n, const int *bufs)
+{
+ QVarLengthArray<GLenum, 16> drawBufs(n);
+
+ for (int i = 0; i < n; i++)
+ drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i];
+ m_extraFuncs->glDrawBuffers(n, drawBufs.constData());
+}
+
+UniformType GraphicsHelperES3::uniformTypeFromGLType(GLenum glType)
+{
+ switch (glType) {
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ return UniformType::Sampler;
+ default:
+ return GraphicsHelperES2::uniformTypeFromGLType(glType);
+ }
+}
+
+void GraphicsHelperES3::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+ m_extraFuncs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+
+void GraphicsHelperES3::bindBufferBase(GLenum target, GLuint index, GLuint buffer)
+{
+ m_extraFuncs->glBindBufferBase(target, index, buffer);
+}
+
+void GraphicsHelperES3::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+ m_extraFuncs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding);
+}
+
+void GraphicsHelperES3::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer)
+{
+ char *bufferData = buffer.data();
+
+ switch (description.m_type) {
+
+ case GL_FLOAT: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_FLOAT_VEC2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_FLOAT_VEC3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_FLOAT_VEC4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3);
+ break;
+ }
+
+ case GL_INT: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_INT_VEC2: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_INT_VEC3: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_INT_VEC4: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_UNSIGNED_INT: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC2: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC3: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC4: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_BOOL: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_BOOL_VEC2: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_BOOL_VEC3: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_BOOL_VEC4: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ // note: only GLES 3.0 supported types, not the same as OpenGL proper
+ // (also, no MS samplers before ES 3.1)
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ {
+ Q_ASSERT(description.m_size == 1);
+ int value = v.toInt();
+ QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1);
+ break;
+ }
+
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name;
+ break;
+ }
+}
+
+char *GraphicsHelperES3::mapBuffer(GLenum target, GLsizeiptr size)
+{
+ return static_cast<char*>(m_extraFuncs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
+}
+
+GLboolean GraphicsHelperES3::unmapBuffer(GLenum target)
+{
+ return m_extraFuncs->glUnmapBuffer(target);
+}
+
+QVector<ShaderUniform> GraphicsHelperES3::programUniformsAndLocations(GLuint programId)
+{
+ QVector<ShaderUniform> uniforms;
+
+ GLint nbrActiveUniforms = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms);
+ uniforms.reserve(nbrActiveUniforms);
+ char uniformName[256];
+ for (GLint i = 0; i < nbrActiveUniforms; i++) {
+ ShaderUniform uniform;
+ GLsizei uniformNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength,
+ &uniform.m_size, &uniform.m_type, uniformName);
+ uniformName[sizeof(uniformName) - 1] = '\0';
+ uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName);
+ uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength);
+ m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex);
+ m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset);
+ m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride);
+ m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride);
+ uniform.m_rawByteSize = uniformByteSize(uniform);
+ uniforms.append(uniform);
+ qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size
+ << " offset" << uniform.m_offset
+ << " rawSize" << uniform.m_rawByteSize;
+ }
+
+ return uniforms;
+}
+
+QVector<ShaderUniformBlock> GraphicsHelperES3::programUniformBlocks(GLuint programId)
+{
+ QVector<ShaderUniformBlock> blocks;
+ GLint nbrActiveUniformsBlocks = 0;
+ m_extraFuncs->glGetProgramiv(programId, GL_ACTIVE_UNIFORM_BLOCKS, &nbrActiveUniformsBlocks);
+ blocks.reserve(nbrActiveUniformsBlocks);
+ for (GLint i = 0; i < nbrActiveUniformsBlocks; i++) {
+ QByteArray uniformBlockName(256, '\0');
+ GLsizei length = 0;
+ ShaderUniformBlock uniformBlock;
+ m_extraFuncs->glGetActiveUniformBlockName(programId, i, 256, &length, uniformBlockName.data());
+ uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length));
+ uniformBlock.m_index = i;
+ m_extraFuncs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformBlock.m_activeUniformsCount);
+ m_extraFuncs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_BINDING, &uniformBlock.m_binding);
+ m_extraFuncs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_DATA_SIZE, &uniformBlock.m_size);
+ blocks.append(uniformBlock);
+ }
+ return blocks;
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp
new file mode 100644
index 000000000..6290d091d
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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 "graphicshelperes3_2_p.h"
+#include <QOpenGLExtraFunctions>
+#include <Qt3DRender/qrendertargetoutput.h>
+#include <private/attachmentpack_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+#ifndef GL_DEPTH_STENCIL_ATTACHMENT
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#endif
+
+namespace Qt3DRender {
+namespace Render {
+
+GraphicsHelperES3_2::GraphicsHelperES3_2()
+{
+}
+
+GraphicsHelperES3_2::~GraphicsHelperES3_2()
+{
+}
+
+bool GraphicsHelperES3_2::frameBufferNeedsRenderBuffer(const Attachment &attachment)
+{
+ Q_UNUSED(attachment);
+ // This is first ES version where we have glFramebufferTexture, so
+ // attaching a D24S8 texture to the combined depth-stencil attachment point
+ // should work.
+ return false;
+}
+
+void GraphicsHelperES3_2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment)
+{
+ GLenum attr = GL_COLOR_ATTACHMENT0;
+
+ if (attachment.m_point <= QRenderTargetOutput::Color15)
+ attr = GL_COLOR_ATTACHMENT0 + attachment.m_point;
+ else if (attachment.m_point == QRenderTargetOutput::Depth)
+ attr = GL_DEPTH_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::Stencil)
+ attr = GL_STENCIL_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::DepthStencil)
+ attr = GL_DEPTH_STENCIL_ATTACHMENT;
+ else
+ qCritical() << "Unsupported FBO attachment OpenGL ES 3.2";
+
+ const QOpenGLTexture::Target target = texture->target();
+
+ texture->bind();
+ if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face != QAbstractTexture::AllFaces)
+ m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel);
+ else
+ m_extraFuncs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel);
+ texture->release();
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h
new file mode 100644
index 000000000..018db6481
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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_GRAPHICSHELPERES3_2_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERES3_2_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/graphicshelperes3_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class GraphicsHelperES3_2 : public GraphicsHelperES3
+{
+public:
+ GraphicsHelperES3_2();
+ ~GraphicsHelperES3_2();
+
+ // QGraphicHelperInterface interface
+ void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override;
+ bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES3_2_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h
new file mode 100644
index 000000000..9bca2d48d
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Svenn-Arne Dragly.
+** 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_GRAPHICSHELPERES3_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERES3_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/graphicshelperes2_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class GraphicsHelperES3 : public GraphicsHelperES2
+{
+public:
+ GraphicsHelperES3();
+ ~GraphicsHelperES3();
+
+ // QGraphicHelperInterface interface
+ void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+ void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override;
+ void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override;
+ void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override;
+ void drawBuffers(GLsizei n, const int *bufs) override;
+ void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override;
+ void readBuffer(GLenum mode) override;
+ void drawBuffer(GLenum mode) override;
+ void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override;
+ char *mapBuffer(GLenum target, GLsizeiptr size) override;
+ QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override;
+ QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override;
+ bool supportsFeature(Feature feature) const override;
+ GLboolean unmapBuffer(GLenum target) override;
+ void vertexAttribDivisor(GLuint index, GLuint divisor) override;
+ void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override;
+
+ UniformType uniformTypeFromGLType(GLenum glType) override;
+
+protected:
+ QOpenGLExtraFunctions *m_extraFuncs = nullptr;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES3_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp
new file mode 100644
index 000000000..6da8a9b6f
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp
@@ -0,0 +1,882 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "graphicshelpergl2_p.h"
+#ifndef QT_OPENGL_ES_2
+#include <QOpenGLFunctions_2_0>
+#include <private/attachmentpack_p.h>
+#include <QtOpenGLExtensions/QOpenGLExtensions>
+#include <private/qgraphicsutils_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+GraphicsHelperGL2::GraphicsHelperGL2()
+ : m_funcs(nullptr)
+ , m_fboFuncs(nullptr)
+{
+
+}
+
+void GraphicsHelperGL2::initializeHelper(QOpenGLContext *context,
+ QAbstractOpenGLFunctions *functions)
+{
+ Q_UNUSED(context);
+ m_funcs = static_cast<QOpenGLFunctions_2_0*>(functions);
+ const bool ok = m_funcs->initializeOpenGLFunctions();
+ Q_ASSERT(ok);
+ Q_UNUSED(ok);
+ if (context->hasExtension(QByteArrayLiteral("GL_ARB_framebuffer_object"))) {
+ m_fboFuncs = new QOpenGLExtension_ARB_framebuffer_object();
+ const bool extensionOk = m_fboFuncs->initializeOpenGLFunctions();
+ Q_ASSERT(extensionOk);
+ Q_UNUSED(extensionOk);
+ }
+}
+
+void GraphicsHelperGL2::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLsizei instances,
+ GLint baseVertex,
+ GLint baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2";
+
+ if (baseVertex != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertex is not supported with OpenGL ES 2";
+
+ for (GLint i = 0; i < instances; i++)
+ drawElements(primitiveType,
+ primitiveCount,
+ indexType,
+ indices);
+}
+
+void GraphicsHelperGL2::drawArraysInstanced(GLenum primitiveType,
+ GLint first,
+ GLsizei count,
+ GLsizei instances)
+{
+ for (GLint i = 0; i < instances; i++)
+ drawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperGL2::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL 2";
+ for (GLint i = 0; i < instances; i++)
+ drawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperGL2::drawElements(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLint baseVertex)
+{
+ if (baseVertex != 0)
+ qWarning() << "glDrawElementsBaseVertex is not supported with OpenGL 2";
+
+ m_funcs->glDrawElements(primitiveType,
+ primitiveCount,
+ indexType,
+ indices);
+}
+
+void GraphicsHelperGL2::drawArrays(GLenum primitiveType,
+ GLint first,
+ GLsizei count)
+{
+ m_funcs->glDrawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperGL2::drawElementsIndirect(GLenum, GLenum, void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL 2";
+}
+
+void GraphicsHelperGL2::drawArraysIndirect(GLenum , void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL 2";
+}
+
+void GraphicsHelperGL2::setVerticesPerPatch(GLint verticesPerPatch)
+{
+ Q_UNUSED(verticesPerPatch);
+ qWarning() << "Tessellation not supported with OpenGL 2";
+}
+
+void GraphicsHelperGL2::useProgram(GLuint programId)
+{
+ m_funcs->glUseProgram(programId);
+}
+
+QVector<ShaderUniform> GraphicsHelperGL2::programUniformsAndLocations(GLuint programId)
+{
+ QVector<ShaderUniform> uniforms;
+
+ GLint nbrActiveUniforms = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms);
+ uniforms.reserve(nbrActiveUniforms);
+ char uniformName[256];
+ for (GLint i = 0; i < nbrActiveUniforms; i++) {
+ ShaderUniform uniform;
+ GLsizei uniformNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength,
+ &uniform.m_size, &uniform.m_type, uniformName);
+ uniformName[sizeof(uniformName) - 1] = '\0';
+ uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName);
+ uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength);
+ // Work around for uniform array names that aren't returned with [0] by some drivers
+ if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]")))
+ uniform.m_name.append(QLatin1String("[0]"));
+ uniform.m_rawByteSize = uniformByteSize(uniform);
+ uniforms.append(uniform);
+ }
+ return uniforms;
+}
+
+QVector<ShaderAttribute> GraphicsHelperGL2::programAttributesAndLocations(GLuint programId)
+{
+ QVector<ShaderAttribute> attributes;
+ GLint nbrActiveAttributes = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes);
+ attributes.reserve(nbrActiveAttributes);
+ char attributeName[256];
+ for (GLint i = 0; i < nbrActiveAttributes; i++) {
+ ShaderAttribute attribute;
+ GLsizei attributeNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength,
+ &attribute.m_size, &attribute.m_type, attributeName);
+ attributeName[sizeof(attributeName) - 1] = '\0';
+ attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName);
+ attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength);
+ attributes.append(attribute);
+ }
+ return attributes;
+}
+
+QVector<ShaderUniformBlock> GraphicsHelperGL2::programUniformBlocks(GLuint programId)
+{
+ Q_UNUSED(programId);
+ QVector<ShaderUniformBlock> blocks;
+ qWarning() << "UBO are not supported by OpenGL 2.0 (since OpenGL 3.1)";
+ return blocks;
+}
+
+QVector<ShaderStorageBlock> GraphicsHelperGL2::programShaderStorageBlocks(GLuint programId)
+{
+ Q_UNUSED(programId);
+ qWarning() << "SSBO are not supported by OpenGL 2.0 (since OpenGL 4.3)";
+ return QVector<ShaderStorageBlock>();
+}
+
+void GraphicsHelperGL2::vertexAttribDivisor(GLuint index,
+ GLuint divisor)
+{
+ Q_UNUSED(index);
+ Q_UNUSED(divisor);
+}
+
+void GraphicsHelperGL2::vertexAttributePointer(GLenum shaderDataType,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer)
+{
+ switch (shaderDataType) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_MAT4:
+ m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ break;
+
+ default:
+ qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type";
+ Q_UNREACHABLE();
+ }
+}
+
+void GraphicsHelperGL2::readBuffer(GLenum mode)
+{
+ m_funcs->glReadBuffer(mode);
+}
+
+void GraphicsHelperGL2::drawBuffer(GLenum mode)
+{
+ m_funcs->glDrawBuffer(mode);
+}
+
+void GraphicsHelperGL2::blendEquation(GLenum mode)
+{
+ m_funcs->glBlendEquation(mode);
+}
+
+void GraphicsHelperGL2::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sfactor);
+ Q_UNUSED(dfactor);
+
+ qWarning() << "glBlendFunci() not supported by OpenGL 2.0 (since OpenGL 4.0)";
+}
+
+void GraphicsHelperGL2::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sRGB);
+ Q_UNUSED(dRGB);
+ Q_UNUSED(sAlpha);
+ Q_UNUSED(dAlpha);
+
+ qWarning() << "glBlendFuncSeparatei() not supported by OpenGL 2.0 (since OpenGL 4.0)";
+}
+
+void GraphicsHelperGL2::alphaTest(GLenum mode1, GLenum mode2)
+{
+ m_funcs->glEnable(GL_ALPHA_TEST);
+ m_funcs->glAlphaFunc(mode1, mode2);
+}
+
+void GraphicsHelperGL2::depthTest(GLenum mode)
+{
+ m_funcs->glEnable(GL_DEPTH_TEST);
+ m_funcs->glDepthFunc(mode);
+}
+
+void GraphicsHelperGL2::depthMask(GLenum mode)
+{
+ m_funcs->glDepthMask(mode);
+}
+
+void GraphicsHelperGL2::frontFace(GLenum mode)
+{
+ m_funcs->glFrontFace(mode);
+}
+
+void GraphicsHelperGL2::setMSAAEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_MULTISAMPLE)
+ : m_funcs->glDisable(GL_MULTISAMPLE);
+}
+
+void GraphicsHelperGL2::setAlphaCoverageEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)
+ : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+}
+
+GLuint GraphicsHelperGL2::createFrameBufferObject()
+{
+ if (m_fboFuncs != nullptr) {
+ GLuint id;
+ m_fboFuncs->glGenFramebuffers(1, &id);
+ return id;
+ }
+ qWarning() << "FBO not supported by your OpenGL hardware";
+ return 0;
+}
+
+void GraphicsHelperGL2::releaseFrameBufferObject(GLuint frameBufferId)
+{
+ if (m_fboFuncs != nullptr)
+ m_fboFuncs->glDeleteFramebuffers(1, &frameBufferId);
+ else
+ qWarning() << "FBO not supported by your OpenGL hardware";
+}
+
+bool GraphicsHelperGL2::checkFrameBufferComplete()
+{
+ if (m_fboFuncs != nullptr)
+ return (m_fboFuncs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+ return false;
+}
+
+bool GraphicsHelperGL2::frameBufferNeedsRenderBuffer(const Attachment &attachment)
+{
+ Q_UNUSED(attachment);
+ return false;
+}
+
+void GraphicsHelperGL2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment)
+{
+ if (m_fboFuncs != nullptr) {
+ GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT;
+
+ if (attachment.m_point <= QRenderTargetOutput::Color15)
+ attr = GL_COLOR_ATTACHMENT0 + attachment.m_point;
+ else if (attachment.m_point == QRenderTargetOutput::Depth)
+ attr = GL_DEPTH_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::Stencil)
+ attr = GL_STENCIL_ATTACHMENT;
+ else
+ qCritical() << "DepthStencil Attachment not supported on OpenGL 2.0";
+
+ const QOpenGLTexture::Target target = texture->target();
+
+ if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face == QAbstractTexture::AllFaces) {
+ qWarning() << "OpenGL 2.0 doesn't handle attaching all the faces of a cube map texture at once to an FBO";
+ return;
+ }
+
+ texture->bind();
+ if (target == QOpenGLTexture::Target3D)
+ m_fboFuncs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel, attachment.m_layer);
+ else if (target == QOpenGLTexture::TargetCubeMap)
+ m_fboFuncs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel);
+ else if (target == QOpenGLTexture::Target1D)
+ m_fboFuncs->glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel);
+ else if (target == QOpenGLTexture::Target2D || target == QOpenGLTexture::TargetRectangle)
+ m_fboFuncs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel);
+ else
+ qCritical() << "Texture format not supported for Attachment on OpenGL 2.0";
+ texture->release();
+ }
+}
+
+void GraphicsHelperGL2::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment)
+{
+ Q_UNUSED(renderBuffer);
+ Q_UNUSED(attachment);
+ Q_UNREACHABLE();
+}
+
+bool GraphicsHelperGL2::supportsFeature(GraphicsHelperInterface::Feature feature) const
+{
+ switch (feature) {
+ case MRT:
+ return (m_fboFuncs != nullptr);
+ case TextureDimensionRetrieval:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void GraphicsHelperGL2::drawBuffers(GLsizei n, const int *bufs)
+{
+ QVarLengthArray<GLenum, 16> drawBufs(n);
+
+ for (int i = 0; i < n; i++)
+ drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i];
+ m_funcs->glDrawBuffers(n, drawBufs.constData());
+}
+
+void GraphicsHelperGL2::bindFragDataLocation(GLuint, const QHash<QString, int> &)
+{
+ qCritical() << "bindFragDataLocation is not supported by GL 2.0";
+}
+
+void GraphicsHelperGL2::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode)
+{
+ if (m_fboFuncs != nullptr) {
+ switch (mode) {
+ case FBODraw:
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBORead:
+ m_fboFuncs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBOReadAndDraw:
+ default:
+ m_fboFuncs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId);
+ return;
+ }
+ } else {
+ qWarning() << "FBO not supported by your OpenGL hardware";
+ }
+}
+
+GLuint GraphicsHelperGL2::boundFrameBufferObject()
+{
+ GLint id = 0;
+ m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id);
+ return id;
+}
+
+void GraphicsHelperGL2::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+ Q_UNUSED(programId);
+ Q_UNUSED(uniformBlockIndex);
+ Q_UNUSED(uniformBlockBinding);
+ qWarning() << "UBO are not supported by OpenGL 2.0 (since OpenGL 3.1)";
+}
+
+void GraphicsHelperGL2::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding)
+{
+ Q_UNUSED(programId);
+ Q_UNUSED(shaderStorageBlockIndex);
+ Q_UNUSED(shaderStorageBlockBinding);
+ qWarning() << "SSBO are not supported by OpenGL 2.0 (since OpenGL 4.3)";
+}
+
+void GraphicsHelperGL2::bindBufferBase(GLenum target, GLuint index, GLuint buffer)
+{
+ Q_UNUSED(target);
+ Q_UNUSED(index);
+ Q_UNUSED(buffer);
+ qWarning() << "bindBufferBase is not supported by OpenGL 2.0 (since OpenGL 3.0)";
+}
+
+void GraphicsHelperGL2::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer)
+{
+ Q_UNUSED(v);
+ Q_UNUSED(description);
+ Q_UNUSED(buffer);
+ qWarning() << "UBO are not supported by OpenGL 2.0 (since OpenGL 3.1)";
+}
+
+uint GraphicsHelperGL2::uniformByteSize(const ShaderUniform &description)
+{
+ uint rawByteSize = 0;
+ int arrayStride = qMax(description.m_arrayStride, 0);
+ int matrixStride = qMax(description.m_matrixStride, 0);
+
+ switch (description.m_type) {
+
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ rawByteSize = 8;
+ break;
+
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ rawByteSize = 12;
+ break;
+
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ rawByteSize = 16;
+ break;
+
+ case GL_FLOAT_MAT2:
+ rawByteSize = matrixStride ? 2 * matrixStride : 16;
+ break;
+
+ case GL_FLOAT_MAT2x4:
+ rawByteSize = matrixStride ? 2 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT4x2:
+ rawByteSize = matrixStride ? 4 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT3:
+ rawByteSize = matrixStride ? 3 * matrixStride : 36;
+ break;
+
+ case GL_FLOAT_MAT2x3:
+ rawByteSize = matrixStride ? 2 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT3x2:
+ rawByteSize = matrixStride ? 3 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT4:
+ rawByteSize = matrixStride ? 4 * matrixStride : 64;
+ break;
+
+ case GL_FLOAT_MAT4x3:
+ rawByteSize = matrixStride ? 4 * matrixStride : 48;
+ break;
+
+ case GL_FLOAT_MAT3x4:
+ rawByteSize = matrixStride ? 3 * matrixStride : 48;
+ break;
+
+ case GL_BOOL:
+ rawByteSize = 1;
+ break;
+
+ case GL_BOOL_VEC2:
+ rawByteSize = 2;
+ break;
+
+ case GL_BOOL_VEC3:
+ rawByteSize = 3;
+ break;
+
+ case GL_BOOL_VEC4:
+ rawByteSize = 4;
+ break;
+
+ case GL_INT:
+ case GL_FLOAT:
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ rawByteSize = 4;
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ }
+
+ return arrayStride ? rawByteSize * arrayStride : rawByteSize;
+}
+
+void GraphicsHelperGL2::enableClipPlane(int clipPlane)
+{
+ m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL2::disableClipPlane(int clipPlane)
+{
+ m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL2::setClipPlane(int clipPlane, const QVector3D &normal, float distance)
+{
+ double plane[4];
+ plane[0] = normal.x();
+ plane[1] = normal.y();
+ plane[2] = normal.z();
+ plane[3] = distance;
+
+ m_funcs->glClipPlane(GL_CLIP_PLANE0 + clipPlane, plane);
+}
+
+GLint GraphicsHelperGL2::maxClipPlaneCount()
+{
+ GLint max = 0;
+ m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max);
+ return max;
+}
+
+void GraphicsHelperGL2::memoryBarrier(QMemoryBarrier::Operations barriers)
+{
+ Q_UNUSED(barriers);
+ qWarning() << "memory barrier is not supported by OpenGL 2.0 (since 4.3)";
+}
+
+void GraphicsHelperGL2::enablePrimitiveRestart(int)
+{
+}
+
+void GraphicsHelperGL2::enableVertexAttributeArray(int location)
+{
+ m_funcs->glEnableVertexAttribArray(location);
+}
+
+void GraphicsHelperGL2::disablePrimitiveRestart()
+{
+}
+
+void GraphicsHelperGL2::clearBufferf(GLint drawbuffer, const QVector4D &values)
+{
+ Q_UNUSED(drawbuffer);
+ Q_UNUSED(values);
+ qWarning() << "glClearBuffer*() not supported by OpenGL 2.0";
+}
+
+void GraphicsHelperGL2::pointSize(bool programmable, GLfloat value)
+{
+ m_funcs->glEnable(GL_POINT_SPRITE);
+ if (programmable)
+ m_funcs->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ else
+ m_funcs->glPointSize(value);
+}
+
+void GraphicsHelperGL2::enablei(GLenum cap, GLuint index)
+{
+ Q_UNUSED(cap);
+ Q_UNUSED(index);
+ qWarning() << "glEnablei() not supported by OpenGL 2.0 (since 3.0)";
+}
+
+void GraphicsHelperGL2::disablei(GLenum cap, GLuint index)
+{
+ Q_UNUSED(cap);
+ Q_UNUSED(index);
+ qWarning() << "glDisablei() not supported by OpenGL 2.0 (since 3.0)";
+}
+
+void GraphicsHelperGL2::setSeamlessCubemap(bool enable)
+{
+ Q_UNUSED(enable);
+ qWarning() << "GL_TEXTURE_CUBE_MAP_SEAMLESS not supported by OpenGL 2.0 (since 3.2)";
+}
+
+QSize GraphicsHelperGL2::getRenderBufferDimensions(GLuint renderBufferId)
+{
+ Q_UNUSED(renderBufferId);
+ qCritical() << "RenderBuffer dimensions retrival not supported on OpenGL 2.0";
+ return QSize(0,0);
+}
+
+QSize GraphicsHelperGL2::getTextureDimensions(GLuint textureId, GLenum target, uint level)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindTexture(target, textureId);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
+ m_funcs->glBindTexture(target, 0);
+
+ return QSize(width, height);
+}
+
+void GraphicsHelperGL2::dispatchCompute(GLuint wx, GLuint wy, GLuint wz)
+{
+ Q_UNUSED(wx);
+ Q_UNUSED(wy);
+ Q_UNUSED(wz);
+ qWarning() << "Compute Shaders are not supported by OpenGL 2.0 (since OpenGL 4.3)";
+}
+
+char *GraphicsHelperGL2::mapBuffer(GLenum target, GLsizeiptr size)
+{
+ Q_UNUSED(size);
+ return static_cast<char*>(m_funcs->glMapBuffer(target, GL_READ_WRITE));
+}
+
+GLboolean GraphicsHelperGL2::unmapBuffer(GLenum target)
+{
+ return m_funcs->glUnmapBuffer(target);
+}
+
+void GraphicsHelperGL2::glUniform1fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform1fv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform2fv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform3fv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform4fv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform1iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform1iv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform2iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform2iv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform3iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform3iv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform4iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform4iv(location, count, values);
+}
+
+void GraphicsHelperGL2::glUniform1uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform1uiv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniform2uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform2uiv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniform3uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform3uiv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniform4uiv(GLint , GLsizei , const GLuint *)
+{
+ qWarning() << "glUniform4uiv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL2::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3fv(location, count, false, values);
+}
+
+void GraphicsHelperGL2::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL2::glUniformMatrix2x3fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix2x3fv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniformMatrix3x2fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix3x2fv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniformMatrix2x4fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix2x4fv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniformMatrix4x2fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix4x2fv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniformMatrix3x4fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix3x4fv not supported by GL 2";
+}
+
+void GraphicsHelperGL2::glUniformMatrix4x3fv(GLint , GLsizei , const GLfloat *)
+{
+ qWarning() << "glUniformMatrix4x3fv not supported by GL 2";
+}
+
+UniformType GraphicsHelperGL2::uniformTypeFromGLType(GLenum type)
+{
+ switch (type) {
+ case GL_FLOAT:
+ return UniformType::Float;
+ case GL_FLOAT_VEC2:
+ return UniformType::Vec2;
+ case GL_FLOAT_VEC3:
+ return UniformType::Vec3;
+ case GL_FLOAT_VEC4:
+ return UniformType::Vec4;
+ case GL_FLOAT_MAT2:
+ return UniformType::Mat2;
+ case GL_FLOAT_MAT3:
+ return UniformType::Mat3;
+ case GL_FLOAT_MAT4:
+ return UniformType::Mat4;
+ case GL_INT:
+ return UniformType::Int;
+ case GL_INT_VEC2:
+ return UniformType::IVec2;
+ case GL_INT_VEC3:
+ return UniformType::IVec3;
+ case GL_INT_VEC4:
+ return UniformType::IVec4;
+ case GL_BOOL:
+ return UniformType::Bool;
+ case GL_BOOL_VEC2:
+ return UniformType::BVec2;
+ case GL_BOOL_VEC3:
+ return UniformType::BVec3;
+ case GL_BOOL_VEC4:
+ return UniformType::BVec4;
+
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_3D:
+ return UniformType::Sampler;
+
+ default:
+ Q_UNREACHABLE();
+ return UniformType::Float;
+ }
+}
+
+void GraphicsHelperGL2::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+ Q_UNUSED(srcX0);
+ Q_UNUSED(srcX1);
+ Q_UNUSED(srcY0);
+ Q_UNUSED(srcY1);
+ Q_UNUSED(dstX0);
+ Q_UNUSED(dstX1);
+ Q_UNUSED(dstY0);
+ Q_UNUSED(dstY1);
+ Q_UNUSED(mask);
+ Q_UNUSED(filter);
+ qWarning() << "Framebuffer blits are not supported by ES 2.0 (since ES 3.1)";
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h
new file mode 100644
index 000000000..2db75004f
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL2_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERGL2_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/graphicshelperinterface_p.h>
+
+#ifndef QT_OPENGL_ES_2
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLFunctions_2_0;
+class QOpenGLExtension_ARB_framebuffer_object;
+
+namespace Qt3DRender {
+namespace Render {
+
+class Q_AUTOTEST_EXPORT GraphicsHelperGL2 : public GraphicsHelperInterface
+{
+public:
+ GraphicsHelperGL2();
+
+ // QGraphicHelperInterface interface
+ void alphaTest(GLenum mode1, GLenum mode2) override;
+ void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+ void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override;
+ bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override;
+ void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override;
+ void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override;
+ void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override;
+ void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override;
+ void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+ void blendEquation(GLenum mode) override;
+ void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override;
+ void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override;
+ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override;
+ GLuint boundFrameBufferObject() override;
+ void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override;
+ bool checkFrameBufferComplete() override;
+ void clearBufferf(GLint drawbuffer, const QVector4D &values) override;
+ GLuint createFrameBufferObject() override;
+ void depthMask(GLenum mode) override;
+ void depthTest(GLenum mode) override;
+ void disableClipPlane(int clipPlane) override;
+ void disablei(GLenum cap, GLuint index) override;
+ void disablePrimitiveRestart() override;
+ void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override;
+ char *mapBuffer(GLenum target, GLsizeiptr size) override;
+ GLboolean unmapBuffer(GLenum target) override;
+ void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override;
+ void drawArraysIndirect(GLenum mode,void *indirect) override;
+ void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override;
+ void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override;
+ void drawBuffers(GLsizei n, const int *bufs) override;
+ void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override;
+ void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override;
+ void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override;
+ void enableClipPlane(int clipPlane) override;
+ void enablei(GLenum cap, GLuint index) override;
+ void enablePrimitiveRestart(int primitiveRestartIndex) override;
+ void enableVertexAttributeArray(int location) override;
+ void frontFace(GLenum mode) override;
+ QSize getRenderBufferDimensions(GLuint renderBufferId) override;
+ QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override;
+ void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override;
+ void pointSize(bool programmable, GLfloat value) override;
+ GLint maxClipPlaneCount() override;
+ void memoryBarrier(QMemoryBarrier::Operations barriers) override;
+ QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override;
+ QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override;
+ QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override;
+ QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override;
+ void releaseFrameBufferObject(GLuint frameBufferId) override;
+ void setMSAAEnabled(bool enable) override;
+ void setAlphaCoverageEnabled(bool enable) override;
+ void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override;
+ void setSeamlessCubemap(bool enable) override;
+ void setVerticesPerPatch(GLint verticesPerPatch) override;
+ bool supportsFeature(Feature feature) const override;
+ uint uniformByteSize(const ShaderUniform &description) override;
+ void useProgram(GLuint programId) override;
+ void vertexAttribDivisor(GLuint index, GLuint divisor) override;
+ void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override;
+ void readBuffer(GLenum mode) override;
+ void drawBuffer(GLenum mode) override;
+
+ void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ void glUniform1iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform2iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform3iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform4iv(GLint location, GLsizei count, const GLint *value) override;
+
+ void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override;
+
+ void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ UniformType uniformTypeFromGLType(GLenum glType) override;
+
+private:
+ QOpenGLFunctions_2_0 *m_funcs;
+ QOpenGLExtension_ARB_framebuffer_object *m_fboFuncs;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL2_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp
new file mode 100644
index 000000000..a35c4e37f
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp
@@ -0,0 +1,1188 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "graphicshelpergl3_2_p.h"
+
+#ifndef QT_OPENGL_ES_2
+#include <QOpenGLFunctions_3_2_Core>
+#include <QOpenGLFunctions_3_3_Core>
+#include <QtOpenGLExtensions/qopenglextensions.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <private/attachmentpack_p.h>
+#include <private/qgraphicsutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+# ifndef QT_OPENGL_3
+# define GL_PATCH_VERTICES 36466
+# define GL_ACTIVE_RESOURCES 0x92F5
+# define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
+# define GL_BUFFER_BINDING 0x9302
+# define GL_BUFFER_DATA_SIZE 0x9303
+# define GL_NUM_ACTIVE_VARIABLES 0x9304
+# define GL_SHADER_STORAGE_BLOCK 0x92E6
+# define GL_UNIFORM 0x92E1
+# define GL_UNIFORM_BLOCK 0x92E2
+# define GL_UNIFORM_BLOCK_INDEX 0x8A3A
+# define GL_UNIFORM_OFFSET 0x8A3B
+# define GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+# define GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+# define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+# define GL_UNIFORM_BLOCK_BINDING 0x8A3F
+# define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+# endif
+
+namespace Qt3DRender {
+namespace Render {
+
+GraphicsHelperGL3_2::GraphicsHelperGL3_2()
+ : m_funcs(nullptr)
+ , m_tessFuncs()
+{
+}
+
+GraphicsHelperGL3_2::~GraphicsHelperGL3_2()
+{
+}
+
+void GraphicsHelperGL3_2::initializeHelper(QOpenGLContext *context,
+ QAbstractOpenGLFunctions *functions)
+{
+ m_funcs = static_cast<QOpenGLFunctions_3_2_Core*>(functions);
+ const bool ok = m_funcs->initializeOpenGLFunctions();
+ Q_ASSERT(ok);
+ Q_UNUSED(ok);
+
+ if (context->hasExtension(QByteArrayLiteral("GL_ARB_tessellation_shader"))) {
+ m_tessFuncs.reset(new QOpenGLExtension_ARB_tessellation_shader);
+ m_tessFuncs->initializeOpenGLFunctions();
+ }
+}
+
+void GraphicsHelperGL3_2::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLsizei instances,
+ GLint baseVertex,
+ GLint baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2";
+
+ // glDrawElements OpenGL 3.1 or greater
+ m_funcs->glDrawElementsInstancedBaseVertex(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ instances,
+ baseVertex);
+}
+
+void GraphicsHelperGL3_2::drawArraysInstanced(GLenum primitiveType,
+ GLint first,
+ GLsizei count,
+ GLsizei instances)
+{
+ // glDrawArraysInstanced OpenGL 3.1 or greater
+ m_funcs->glDrawArraysInstanced(primitiveType,
+ first,
+ count,
+ instances);
+}
+
+void GraphicsHelperGL3_2::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL 3";
+ m_funcs->glDrawArraysInstanced(primitiveType,
+ first,
+ count,
+ instances);
+}
+
+void GraphicsHelperGL3_2::drawElements(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLint baseVertex)
+{
+ m_funcs->glDrawElementsBaseVertex(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ baseVertex);
+}
+
+void GraphicsHelperGL3_2::drawArrays(GLenum primitiveType,
+ GLint first,
+ GLsizei count)
+{
+ m_funcs->glDrawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperGL3_2::drawElementsIndirect(GLenum, GLenum, void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL 3.2";
+}
+
+void GraphicsHelperGL3_2::drawArraysIndirect(GLenum , void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL 3.2";
+}
+
+void GraphicsHelperGL3_2::setVerticesPerPatch(GLint verticesPerPatch)
+{
+#if defined(QT_OPENGL_4)
+ if (!m_tessFuncs) {
+ qWarning() << "Tessellation not supported with OpenGL 3 without GL_ARB_tessellation_shader";
+ return;
+ }
+
+ m_tessFuncs->glPatchParameteri(GL_PATCH_VERTICES, verticesPerPatch);
+#else
+ Q_UNUSED(verticesPerPatch);
+ qWarning() << "Tessellation not supported";
+#endif
+}
+
+void GraphicsHelperGL3_2::useProgram(GLuint programId)
+{
+ m_funcs->glUseProgram(programId);
+}
+
+QVector<ShaderUniform> GraphicsHelperGL3_2::programUniformsAndLocations(GLuint programId)
+{
+ QVector<ShaderUniform> uniforms;
+
+ GLint nbrActiveUniforms = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms);
+ uniforms.reserve(nbrActiveUniforms);
+ char uniformName[256];
+ for (GLint i = 0; i < nbrActiveUniforms; i++) {
+ ShaderUniform uniform;
+ GLsizei uniformNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength,
+ &uniform.m_size, &uniform.m_type, uniformName);
+ uniformName[sizeof(uniformName) - 1] = '\0';
+ uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName);
+ uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength);
+ // Work around for uniform array names that aren't returned with [0] by some drivers
+ if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]")))
+ uniform.m_name.append(QLatin1String("[0]"));
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride);
+ uniform.m_rawByteSize = uniformByteSize(uniform);
+ uniforms.append(uniform);
+ qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size
+ << " offset" << uniform.m_offset
+ << " rawSize" << uniform.m_rawByteSize;
+ }
+
+ return uniforms;
+}
+
+QVector<ShaderAttribute> GraphicsHelperGL3_2::programAttributesAndLocations(GLuint programId)
+{
+ QVector<ShaderAttribute> attributes;
+ GLint nbrActiveAttributes = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes);
+ attributes.reserve(nbrActiveAttributes);
+ char attributeName[256];
+ for (GLint i = 0; i < nbrActiveAttributes; i++) {
+ ShaderAttribute attribute;
+ GLsizei attributeNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength,
+ &attribute.m_size, &attribute.m_type, attributeName);
+ attributeName[sizeof(attributeName) - 1] = '\0';
+ attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName);
+ attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength);
+ attributes.append(attribute);
+ }
+ return attributes;
+}
+
+QVector<ShaderUniformBlock> GraphicsHelperGL3_2::programUniformBlocks(GLuint programId)
+{
+ QVector<ShaderUniformBlock> blocks;
+ GLint nbrActiveUniformsBlocks = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORM_BLOCKS, &nbrActiveUniformsBlocks);
+ blocks.reserve(nbrActiveUniformsBlocks);
+ for (GLint i = 0; i < nbrActiveUniformsBlocks; i++) {
+ QByteArray uniformBlockName(256, '\0');
+ GLsizei length = 0;
+ ShaderUniformBlock uniformBlock;
+ m_funcs->glGetActiveUniformBlockName(programId, i, 256, &length, uniformBlockName.data());
+ uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length));
+ uniformBlock.m_index = i;
+ m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformBlock.m_activeUniformsCount);
+ m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_BINDING, &uniformBlock.m_binding);
+ m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_DATA_SIZE, &uniformBlock.m_size);
+ blocks.append(uniformBlock);
+ }
+ return blocks;
+}
+
+QVector<ShaderStorageBlock> GraphicsHelperGL3_2::programShaderStorageBlocks(GLuint programId)
+{
+ Q_UNUSED(programId);
+ QVector<ShaderStorageBlock> blocks;
+ qWarning() << "SSBO are not supported by OpenGL 3.2 (since OpenGL 4.3)";
+ return blocks;
+}
+
+void GraphicsHelperGL3_2::vertexAttribDivisor(GLuint index, GLuint divisor)
+{
+ Q_UNUSED(index);
+ Q_UNUSED(divisor);
+ qCWarning(Render::Rendering) << "Vertex attribute divisor not available with OpenGL 3.2 core";
+}
+
+void GraphicsHelperGL3_2::vertexAttributePointer(GLenum shaderDataType,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer)
+{
+ switch (shaderDataType) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_MAT4:
+ m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ break;
+
+ case GL_INT:
+ case GL_INT_VEC2:
+ case GL_INT_VEC3:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC4:
+ m_funcs->glVertexAttribIPointer(index, size, type, stride, pointer);
+ break;
+
+ default:
+ qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type";
+ Q_UNREACHABLE();
+ }
+}
+
+void GraphicsHelperGL3_2::readBuffer(GLenum mode)
+{
+ m_funcs->glReadBuffer(mode);
+}
+
+void GraphicsHelperGL3_2::drawBuffer(GLenum mode)
+{
+ m_funcs->glDrawBuffer(mode);
+}
+
+void GraphicsHelperGL3_2::blendEquation(GLenum mode)
+{
+ m_funcs->glBlendEquation(mode);
+}
+
+void GraphicsHelperGL3_2::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sfactor);
+ Q_UNUSED(dfactor);
+
+ qWarning() << "glBlendFunci() not supported by OpenGL 3.0 (since OpenGL 4.0)";
+}
+
+void GraphicsHelperGL3_2::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sRGB);
+ Q_UNUSED(dRGB);
+ Q_UNUSED(sAlpha);
+ Q_UNUSED(dAlpha);
+
+ qWarning() << "glBlendFuncSeparatei() not supported by OpenGL 3.0 (since OpenGL 4.0)";
+}
+
+void GraphicsHelperGL3_2::alphaTest(GLenum, GLenum)
+{
+ qCWarning(Render::Rendering) << "AlphaTest not available with OpenGL 3.2 core";
+}
+
+void GraphicsHelperGL3_2::depthTest(GLenum mode)
+{
+ m_funcs->glEnable(GL_DEPTH_TEST);
+ m_funcs->glDepthFunc(mode);
+}
+
+void GraphicsHelperGL3_2::depthMask(GLenum mode)
+{
+ m_funcs->glDepthMask(mode);
+}
+
+void GraphicsHelperGL3_2::frontFace(GLenum mode)
+{
+ m_funcs->glFrontFace(mode);
+
+}
+
+void GraphicsHelperGL3_2::setMSAAEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_MULTISAMPLE)
+ : m_funcs->glDisable(GL_MULTISAMPLE);
+}
+
+void GraphicsHelperGL3_2::setAlphaCoverageEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)
+ : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+}
+
+GLuint GraphicsHelperGL3_2::createFrameBufferObject()
+{
+ GLuint id;
+ m_funcs->glGenFramebuffers(1, &id);
+ return id;
+}
+
+void GraphicsHelperGL3_2::releaseFrameBufferObject(GLuint frameBufferId)
+{
+ m_funcs->glDeleteFramebuffers(1, &frameBufferId);
+}
+
+void GraphicsHelperGL3_2::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode)
+{
+ switch (mode) {
+ case FBODraw:
+ m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBORead:
+ m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBOReadAndDraw:
+ default:
+ m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId);
+ return;
+ }
+}
+
+GLuint GraphicsHelperGL3_2::boundFrameBufferObject()
+{
+ GLint id = 0;
+ m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id);
+ return id;
+}
+
+bool GraphicsHelperGL3_2::checkFrameBufferComplete()
+{
+ return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+}
+
+bool GraphicsHelperGL3_2::frameBufferNeedsRenderBuffer(const Attachment &attachment)
+{
+ Q_UNUSED(attachment);
+ return false;
+}
+
+void GraphicsHelperGL3_2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment)
+{
+ GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT;
+
+ if (attachment.m_point <= QRenderTargetOutput::Color15)
+ attr = GL_COLOR_ATTACHMENT0 + attachment.m_point;
+ else if (attachment.m_point == QRenderTargetOutput::Depth)
+ attr = GL_DEPTH_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::Stencil)
+ attr = GL_STENCIL_ATTACHMENT;
+
+ texture->bind();
+ QOpenGLTexture::Target target = texture->target();
+ if (target == QOpenGLTexture::Target1DArray || target == QOpenGLTexture::Target2DArray ||
+ target == QOpenGLTexture::Target2DMultisampleArray || target == QOpenGLTexture::Target3D)
+ m_funcs->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel, attachment.m_layer);
+ else if (target == QOpenGLTexture::TargetCubeMapArray)
+ m_funcs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel, attachment.m_layer);
+ else if (target == QOpenGLTexture::TargetCubeMap)
+ m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel);
+ else
+ m_funcs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel);
+ texture->release();
+}
+
+void GraphicsHelperGL3_2::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment)
+{
+ Q_UNUSED(renderBuffer);
+ Q_UNUSED(attachment);
+ Q_UNREACHABLE();
+}
+
+bool GraphicsHelperGL3_2::supportsFeature(GraphicsHelperInterface::Feature feature) const
+{
+ switch (feature) {
+ case MRT:
+ case UniformBufferObject:
+ case PrimitiveRestart:
+ case RenderBufferDimensionRetrieval:
+ case TextureDimensionRetrieval:
+ case BindableFragmentOutputs:
+ case BlitFramebuffer:
+ return true;
+ case Tessellation:
+ return !m_tessFuncs.isNull();
+ default:
+ return false;
+ }
+}
+
+void GraphicsHelperGL3_2::drawBuffers(GLsizei n, const int *bufs)
+{
+ // Use QVarLengthArray here
+ QVarLengthArray<GLenum, 16> drawBufs(n);
+
+ for (int i = 0; i < n; i++)
+ drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i];
+ m_funcs->glDrawBuffers(n, drawBufs.constData());
+}
+
+void GraphicsHelperGL3_2::bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs)
+{
+ for (auto it = outputs.begin(), end = outputs.end(); it != end; ++it)
+ m_funcs->glBindFragDataLocation(shader, it.value(), it.key().toStdString().c_str());
+}
+
+void GraphicsHelperGL3_2::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+ m_funcs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding);
+}
+
+void GraphicsHelperGL3_2::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding)
+{
+ Q_UNUSED(programId);
+ Q_UNUSED(shaderStorageBlockIndex);
+ Q_UNUSED(shaderStorageBlockBinding);
+ qWarning() << "SSBO are not supported by OpenGL 3.0 (since OpenGL 4.3)";
+}
+
+void GraphicsHelperGL3_2::bindBufferBase(GLenum target, GLuint index, GLuint buffer)
+{
+ m_funcs->glBindBufferBase(target, index, buffer);
+}
+
+void GraphicsHelperGL3_2::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer)
+{
+ char *bufferData = buffer.data();
+
+ switch (description.m_type) {
+
+ case GL_FLOAT: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_FLOAT_VEC2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_FLOAT_VEC3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_FLOAT_VEC4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3);
+ break;
+ }
+
+ case GL_INT: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_INT_VEC2: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_INT_VEC3: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_INT_VEC4: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_UNSIGNED_INT: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC2: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC3: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC4: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_BOOL: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_BOOL_VEC2: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_BOOL_VEC3: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_BOOL_VEC4: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_2D_RECT:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_2D_RECT:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_1D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: {
+ Q_ASSERT(description.m_size == 1);
+ int value = v.toInt();
+ QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1);
+ break;
+ }
+
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name;
+ break;
+ }
+}
+
+uint GraphicsHelperGL3_2::uniformByteSize(const ShaderUniform &description)
+{
+ uint rawByteSize = 0;
+ int arrayStride = qMax(description.m_arrayStride, 0);
+ int matrixStride = qMax(description.m_matrixStride, 0);
+
+ switch (description.m_type) {
+
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ rawByteSize = 8;
+ break;
+
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ rawByteSize = 12;
+ break;
+
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ rawByteSize = 16;
+ break;
+
+ case GL_FLOAT_MAT2:
+ rawByteSize = matrixStride ? 2 * matrixStride : 16;
+ break;
+
+ case GL_FLOAT_MAT2x4:
+ rawByteSize = matrixStride ? 2 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT4x2:
+ rawByteSize = matrixStride ? 4 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT3:
+ rawByteSize = matrixStride ? 3 * matrixStride : 36;
+ break;
+
+ case GL_FLOAT_MAT2x3:
+ rawByteSize = matrixStride ? 2 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT3x2:
+ rawByteSize = matrixStride ? 3 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT4:
+ rawByteSize = matrixStride ? 4 * matrixStride : 64;
+ break;
+
+ case GL_FLOAT_MAT4x3:
+ rawByteSize = matrixStride ? 4 * matrixStride : 48;
+ break;
+
+ case GL_FLOAT_MAT3x4:
+ rawByteSize = matrixStride ? 3 * matrixStride : 48;
+ break;
+
+ case GL_BOOL:
+ rawByteSize = 1;
+ break;
+
+ case GL_BOOL_VEC2:
+ rawByteSize = 2;
+ break;
+
+ case GL_BOOL_VEC3:
+ rawByteSize = 3;
+ break;
+
+ case GL_BOOL_VEC4:
+ rawByteSize = 4;
+ break;
+
+ case GL_INT:
+ case GL_FLOAT:
+ case GL_UNSIGNED_INT:
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_2D_RECT:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_2D_RECT:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_1D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ rawByteSize = 4;
+ break;
+ }
+
+ return arrayStride ? rawByteSize * arrayStride : rawByteSize;
+}
+
+void GraphicsHelperGL3_2::enableClipPlane(int clipPlane)
+{
+ m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL3_2::disableClipPlane(int clipPlane)
+{
+ m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL3_2::setClipPlane(int clipPlane, const QVector3D &normal, float distance)
+{
+ // deprecated
+ Q_UNUSED(clipPlane);
+ Q_UNUSED(normal);
+ Q_UNUSED(distance);
+}
+
+GLint GraphicsHelperGL3_2::maxClipPlaneCount()
+{
+ GLint max = 0;
+ m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max);
+ return max;
+}
+
+void GraphicsHelperGL3_2::memoryBarrier(QMemoryBarrier::Operations barriers)
+{
+ Q_UNUSED(barriers);
+ qWarning() << "memory barrier is not supported by OpenGL 3.0 (since 4.3)";
+}
+
+void GraphicsHelperGL3_2::enablePrimitiveRestart(int primitiveRestartIndex)
+{
+ m_funcs->glPrimitiveRestartIndex(primitiveRestartIndex);
+ m_funcs->glEnable(GL_PRIMITIVE_RESTART);
+}
+
+void GraphicsHelperGL3_2::enableVertexAttributeArray(int location)
+{
+ m_funcs->glEnableVertexAttribArray(location);
+}
+
+void GraphicsHelperGL3_2::disablePrimitiveRestart()
+{
+ m_funcs->glDisable(GL_PRIMITIVE_RESTART);
+}
+
+void GraphicsHelperGL3_2::clearBufferf(GLint drawbuffer, const QVector4D &values)
+{
+ GLfloat vec[4] = {values[0], values[1], values[2], values[3]};
+ m_funcs->glClearBufferfv(GL_COLOR, drawbuffer, vec);
+}
+
+void GraphicsHelperGL3_2::pointSize(bool programmable, GLfloat value)
+{
+ if (programmable) {
+ m_funcs->glEnable(GL_PROGRAM_POINT_SIZE);
+ } else {
+ m_funcs->glDisable(GL_PROGRAM_POINT_SIZE);
+ m_funcs->glPointSize(value);
+ }
+}
+
+void GraphicsHelperGL3_2::enablei(GLenum cap, GLuint index)
+{
+ m_funcs->glEnablei(cap, index);
+}
+
+void GraphicsHelperGL3_2::disablei(GLenum cap, GLuint index)
+{
+ m_funcs->glDisablei(cap, index);
+}
+
+void GraphicsHelperGL3_2::setSeamlessCubemap(bool enable)
+{
+ if (enable)
+ m_funcs->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ else
+ m_funcs->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+}
+
+QSize GraphicsHelperGL3_2::getRenderBufferDimensions(GLuint renderBufferId)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+ return QSize(width, height);
+}
+
+QSize GraphicsHelperGL3_2::getTextureDimensions(GLuint textureId, GLenum target, uint level)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindTexture(target, textureId);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
+ m_funcs->glBindTexture(target, 0);
+
+ return QSize(width, height);
+}
+
+void GraphicsHelperGL3_2::dispatchCompute(GLuint wx, GLuint wy, GLuint wz)
+{
+ Q_UNUSED(wx);
+ Q_UNUSED(wy);
+ Q_UNUSED(wz);
+ qWarning() << "Compute Shaders are not supported by OpenGL 3.2 (since OpenGL 4.3)";
+}
+
+char *GraphicsHelperGL3_2::mapBuffer(GLenum target, GLsizeiptr size)
+{
+ return static_cast<char*>(m_funcs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
+}
+
+GLboolean GraphicsHelperGL3_2::unmapBuffer(GLenum target)
+{
+ return m_funcs->glUnmapBuffer(target);
+}
+
+void GraphicsHelperGL3_2::glUniform1fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform1fv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform2fv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform3fv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform4fv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform1iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform1iv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform2iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform2iv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform3iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform3iv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform4iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform4iv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform1uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform1uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform2uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform2uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform3uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform3uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniform4uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform4uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2x3fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3x2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2x4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4x2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3x4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_2::glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4x3fv(location, count, false, values);
+}
+
+UniformType GraphicsHelperGL3_2::uniformTypeFromGLType(GLenum type)
+{
+ switch (type) {
+ case GL_FLOAT:
+ return UniformType::Float;
+ case GL_FLOAT_VEC2:
+ return UniformType::Vec2;
+ case GL_FLOAT_VEC3:
+ return UniformType::Vec3;
+ case GL_FLOAT_VEC4:
+ return UniformType::Vec4;
+ case GL_FLOAT_MAT2:
+ return UniformType::Mat2;
+ case GL_FLOAT_MAT3:
+ return UniformType::Mat3;
+ case GL_FLOAT_MAT4:
+ return UniformType::Mat4;
+ case GL_FLOAT_MAT2x3:
+ return UniformType::Mat2x3;
+ case GL_FLOAT_MAT3x2:
+ return UniformType::Mat3x2;
+ case GL_FLOAT_MAT2x4:
+ return UniformType::Mat2x4;
+ case GL_FLOAT_MAT4x2:
+ return UniformType::Mat4x2;
+ case GL_FLOAT_MAT3x4:
+ return UniformType::Mat3x4;
+ case GL_FLOAT_MAT4x3:
+ return UniformType::Mat4x3;
+ case GL_INT:
+ return UniformType::Int;
+ case GL_INT_VEC2:
+ return UniformType::IVec2;
+ case GL_INT_VEC3:
+ return UniformType::IVec3;
+ case GL_INT_VEC4:
+ return UniformType::IVec4;
+ case GL_UNSIGNED_INT:
+ return UniformType::UInt;
+ case GL_UNSIGNED_INT_VEC2:
+ return UniformType::UIVec2;
+ case GL_UNSIGNED_INT_VEC3:
+ return UniformType::UIVec3;
+ case GL_UNSIGNED_INT_VEC4:
+ return UniformType::UIVec4;
+ case GL_BOOL:
+ return UniformType::Bool;
+ case GL_BOOL_VEC2:
+ return UniformType::BVec2;
+ case GL_BOOL_VEC3:
+ return UniformType::BVec3;
+ case GL_BOOL_VEC4:
+ return UniformType::BVec4;
+
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_2D_RECT:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_SAMPLER_3D:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ return UniformType::Sampler;
+ default:
+ Q_UNREACHABLE();
+ return UniformType::Float;
+ }
+}
+
+void GraphicsHelperGL3_2::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+ m_funcs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h
new file mode 100644
index 000000000..133295fd7
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL3_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERGL3_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/graphicshelperinterface_p.h>
+#include <QtCore/qscopedpointer.h>
+
+#ifndef QT_OPENGL_ES_2
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLFunctions_3_2_Core;
+class QOpenGLExtension_ARB_tessellation_shader;
+
+namespace Qt3DRender {
+namespace Render {
+
+class Q_AUTOTEST_EXPORT GraphicsHelperGL3_2 : public GraphicsHelperInterface
+{
+public:
+ GraphicsHelperGL3_2();
+ ~GraphicsHelperGL3_2();
+
+ // QGraphicHelperInterface interface
+ void alphaTest(GLenum mode1, GLenum mode2) override;
+ void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+ void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override;
+ bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override;
+ void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override;
+ void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override;
+ void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override;
+ void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override;
+ void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+ void blendEquation(GLenum mode) override;
+ void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override;
+ void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override;
+ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override;
+ GLuint boundFrameBufferObject() override;
+ void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override;
+ bool checkFrameBufferComplete() override;
+ void clearBufferf(GLint drawbuffer, const QVector4D &values) override;
+ GLuint createFrameBufferObject() override;
+ void depthMask(GLenum mode) override;
+ void depthTest(GLenum mode) override;
+ void disableClipPlane(int clipPlane) override;
+ void disablei(GLenum cap, GLuint index) override;
+ void disablePrimitiveRestart() override;
+ void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override;
+ char *mapBuffer(GLenum target, GLsizeiptr size) override;
+ GLboolean unmapBuffer(GLenum target) override;
+ void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override;
+ void drawArraysIndirect(GLenum mode,void *indirect) override;
+ void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override;
+ void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override;
+ void drawBuffers(GLsizei n, const int *bufs) override;
+ void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override;
+ void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override;
+ void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override;
+ void enableClipPlane(int clipPlane) override;
+ void enablei(GLenum cap, GLuint index) override;
+ void enablePrimitiveRestart(int primitiveRestartIndex) override;
+ void enableVertexAttributeArray(int location) override;
+ void frontFace(GLenum mode) override;
+ QSize getRenderBufferDimensions(GLuint renderBufferId) override;
+ QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override;
+ void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override;
+ void pointSize(bool programmable, GLfloat value) override;
+ GLint maxClipPlaneCount() override;
+ void memoryBarrier(QMemoryBarrier::Operations barriers) override;
+ QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override;
+ QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override;
+ QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override;
+ QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override;
+ void releaseFrameBufferObject(GLuint frameBufferId) override;
+ void setMSAAEnabled(bool enable) override;
+ void setAlphaCoverageEnabled(bool enable) override;
+ void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override;
+ void setSeamlessCubemap(bool enable) override;
+ void setVerticesPerPatch(GLint verticesPerPatch) override;
+ bool supportsFeature(Feature feature) const override;
+ uint uniformByteSize(const ShaderUniform &description) override;
+ void useProgram(GLuint programId) override;
+ void vertexAttribDivisor(GLuint index, GLuint divisor) override;
+ void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override;
+ void readBuffer(GLenum mode) override;
+ void drawBuffer(GLenum mode) override;
+
+ void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ void glUniform1iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform2iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform3iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform4iv(GLint location, GLsizei count, const GLint *value) override;
+
+ void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override;
+
+ void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ UniformType uniformTypeFromGLType(GLenum glType) override;
+
+private:
+ QOpenGLFunctions_3_2_Core *m_funcs;
+ QScopedPointer<QOpenGLExtension_ARB_tessellation_shader> m_tessFuncs;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL3_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp
new file mode 100644
index 000000000..b2512d84a
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp
@@ -0,0 +1,1184 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "graphicshelpergl3_3_p.h"
+
+#ifndef QT_OPENGL_ES_2
+#include <QOpenGLFunctions_3_3_Core>
+#include <QtOpenGLExtensions/qopenglextensions.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <private/attachmentpack_p.h>
+#include <private/qgraphicsutils_p.h>
+
+# ifndef QT_OPENGL_3_2
+# define GL_PATCH_VERTICES 36466
+# define GL_ACTIVE_RESOURCES 0x92F5
+# define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
+# define GL_BUFFER_BINDING 0x9302
+# define GL_BUFFER_DATA_SIZE 0x9303
+# define GL_NUM_ACTIVE_VARIABLES 0x9304
+# define GL_SHADER_STORAGE_BLOCK 0x92E6
+# define GL_UNIFORM 0x92E1
+# define GL_UNIFORM_BLOCK 0x92E2
+# define GL_UNIFORM_BLOCK_INDEX 0x8A3A
+# define GL_UNIFORM_OFFSET 0x8A3B
+# define GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+# define GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+# define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+# define GL_UNIFORM_BLOCK_BINDING 0x8A3F
+# define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+# endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+GraphicsHelperGL3_3::GraphicsHelperGL3_3()
+ : m_funcs(nullptr)
+ , m_tessFuncs()
+{
+}
+
+GraphicsHelperGL3_3::~GraphicsHelperGL3_3()
+{
+}
+
+void GraphicsHelperGL3_3::initializeHelper(QOpenGLContext *context,
+ QAbstractOpenGLFunctions *functions)
+{
+ m_funcs = static_cast<QOpenGLFunctions_3_3_Core*>(functions);
+ const bool ok = m_funcs->initializeOpenGLFunctions();
+ Q_ASSERT(ok);
+ Q_UNUSED(ok);
+
+ if (context->hasExtension(QByteArrayLiteral("GL_ARB_tessellation_shader"))) {
+ m_tessFuncs.reset(new QOpenGLExtension_ARB_tessellation_shader);
+ m_tessFuncs->initializeOpenGLFunctions();
+ }
+}
+
+void GraphicsHelperGL3_3::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLsizei instances,
+ GLint baseVertex,
+ GLint baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL 3";
+
+ // glDrawElements OpenGL 3.1 or greater
+ m_funcs->glDrawElementsInstancedBaseVertex(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ instances,
+ baseVertex);
+}
+
+void GraphicsHelperGL3_3::drawArraysInstanced(GLenum primitiveType,
+ GLint first,
+ GLsizei count,
+ GLsizei instances)
+{
+ // glDrawArraysInstanced OpenGL 3.1 or greater
+ m_funcs->glDrawArraysInstanced(primitiveType,
+ first,
+ count,
+ instances);
+}
+
+void GraphicsHelperGL3_3::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL 3";
+ m_funcs->glDrawArraysInstanced(primitiveType,
+ first,
+ count,
+ instances);
+}
+
+void GraphicsHelperGL3_3::drawElements(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLint baseVertex)
+{
+ m_funcs->glDrawElementsBaseVertex(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ baseVertex);
+}
+
+void GraphicsHelperGL3_3::drawElementsIndirect(GLenum, GLenum, void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL 3";
+}
+
+void GraphicsHelperGL3_3::drawArrays(GLenum primitiveType,
+ GLint first,
+ GLsizei count)
+{
+ m_funcs->glDrawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperGL3_3::drawArraysIndirect(GLenum , void *)
+{
+ qWarning() << "Indirect Drawing is not supported with OpenGL 3";
+}
+
+void GraphicsHelperGL3_3::setVerticesPerPatch(GLint verticesPerPatch)
+{
+#if defined(QT_OPENGL_4)
+ if (!m_tessFuncs) {
+ qWarning() << "Tessellation not supported with OpenGL 3 without GL_ARB_tessellation_shader";
+ return;
+ }
+
+ m_tessFuncs->glPatchParameteri(GL_PATCH_VERTICES, verticesPerPatch);
+#else
+ Q_UNUSED(verticesPerPatch);
+ qWarning() << "Tessellation not supported";
+#endif
+}
+
+void GraphicsHelperGL3_3::useProgram(GLuint programId)
+{
+ m_funcs->glUseProgram(programId);
+}
+
+QVector<ShaderUniform> GraphicsHelperGL3_3::programUniformsAndLocations(GLuint programId)
+{
+ QVector<ShaderUniform> uniforms;
+
+ GLint nbrActiveUniforms = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms);
+ uniforms.reserve(nbrActiveUniforms);
+ char uniformName[256];
+ for (GLint i = 0; i < nbrActiveUniforms; i++) {
+ ShaderUniform uniform;
+ GLsizei uniformNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength,
+ &uniform.m_size, &uniform.m_type, uniformName);
+ uniformName[sizeof(uniformName) - 1] = '\0';
+ uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName);
+ uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength);
+ // Work around for uniform array names that aren't returned with [0] by some drivers
+ if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]")))
+ uniform.m_name.append(QLatin1String("[0]"));
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride);
+ uniform.m_rawByteSize = uniformByteSize(uniform);
+ uniforms.append(uniform);
+ qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size
+ << " offset" << uniform.m_offset
+ << " rawSize" << uniform.m_rawByteSize;
+ }
+
+ return uniforms;
+}
+
+QVector<ShaderAttribute> GraphicsHelperGL3_3::programAttributesAndLocations(GLuint programId)
+{
+ QVector<ShaderAttribute> attributes;
+ GLint nbrActiveAttributes = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes);
+ attributes.reserve(nbrActiveAttributes);
+ char attributeName[256];
+ for (GLint i = 0; i < nbrActiveAttributes; i++) {
+ ShaderAttribute attribute;
+ GLsizei attributeNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength,
+ &attribute.m_size, &attribute.m_type, attributeName);
+ attributeName[sizeof(attributeName) - 1] = '\0';
+ attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName);
+ attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength);
+ attributes.append(attribute);
+ }
+ return attributes;
+}
+
+QVector<ShaderUniformBlock> GraphicsHelperGL3_3::programUniformBlocks(GLuint programId)
+{
+ QVector<ShaderUniformBlock> blocks;
+ GLint nbrActiveUniformsBlocks = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORM_BLOCKS, &nbrActiveUniformsBlocks);
+ blocks.reserve(nbrActiveUniformsBlocks);
+ for (GLint i = 0; i < nbrActiveUniformsBlocks; i++) {
+ QByteArray uniformBlockName(256, '\0');
+ GLsizei length = 0;
+ ShaderUniformBlock uniformBlock;
+ m_funcs->glGetActiveUniformBlockName(programId, i, 256, &length, uniformBlockName.data());
+ uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length));
+ uniformBlock.m_index = i;
+ m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformBlock.m_activeUniformsCount);
+ m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_BINDING, &uniformBlock.m_binding);
+ m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_DATA_SIZE, &uniformBlock.m_size);
+ blocks.append(uniformBlock);
+ }
+ return blocks;
+}
+
+QVector<ShaderStorageBlock> GraphicsHelperGL3_3::programShaderStorageBlocks(GLuint programId)
+{
+ Q_UNUSED(programId);
+ QVector<ShaderStorageBlock> blocks;
+ qWarning() << "SSBO are not supported by OpenGL 3.3 (since OpenGL 4.3)";
+ return blocks;
+}
+
+void GraphicsHelperGL3_3::vertexAttribDivisor(GLuint index, GLuint divisor)
+{
+ m_funcs->glVertexAttribDivisor(index, divisor);
+}
+
+void GraphicsHelperGL3_3::vertexAttributePointer(GLenum shaderDataType,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer)
+{
+ switch (shaderDataType) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_MAT4:
+ m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ break;
+
+ case GL_INT:
+ case GL_INT_VEC2:
+ case GL_INT_VEC3:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC4:
+ m_funcs->glVertexAttribIPointer(index, size, type, stride, pointer);
+ break;
+
+ default:
+ qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type";
+ }
+}
+
+void GraphicsHelperGL3_3::readBuffer(GLenum mode)
+{
+ m_funcs->glReadBuffer(mode);
+}
+
+void GraphicsHelperGL3_3::drawBuffer(GLenum mode)
+{
+ m_funcs->glDrawBuffer(mode);
+}
+
+void GraphicsHelperGL3_3::blendEquation(GLenum mode)
+{
+ m_funcs->glBlendEquation(mode);
+}
+
+void GraphicsHelperGL3_3::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sfactor);
+ Q_UNUSED(dfactor);
+
+ qWarning() << "glBlendFunci() not supported by OpenGL 3.3 (since OpenGL 4.0)";
+}
+
+void GraphicsHelperGL3_3::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha)
+{
+ Q_UNUSED(buf);
+ Q_UNUSED(sRGB);
+ Q_UNUSED(dRGB);
+ Q_UNUSED(sAlpha);
+ Q_UNUSED(dAlpha);
+
+ qWarning() << "glBlendFuncSeparatei() not supported by OpenGL 3.3 (since OpenGL 4.0)";
+}
+
+void GraphicsHelperGL3_3::alphaTest(GLenum, GLenum)
+{
+ qCWarning(Render::Rendering) << "AlphaTest not available with OpenGL 3.2 core";
+}
+
+void GraphicsHelperGL3_3::depthTest(GLenum mode)
+{
+ m_funcs->glEnable(GL_DEPTH_TEST);
+ m_funcs->glDepthFunc(mode);
+}
+
+void GraphicsHelperGL3_3::depthMask(GLenum mode)
+{
+ m_funcs->glDepthMask(mode);
+}
+
+void GraphicsHelperGL3_3::frontFace(GLenum mode)
+{
+ m_funcs->glFrontFace(mode);
+
+}
+
+void GraphicsHelperGL3_3::setMSAAEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_MULTISAMPLE)
+ : m_funcs->glDisable(GL_MULTISAMPLE);
+}
+
+void GraphicsHelperGL3_3::setAlphaCoverageEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)
+ : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+}
+
+GLuint GraphicsHelperGL3_3::createFrameBufferObject()
+{
+ GLuint id;
+ m_funcs->glGenFramebuffers(1, &id);
+ return id;
+}
+
+void GraphicsHelperGL3_3::releaseFrameBufferObject(GLuint frameBufferId)
+{
+ m_funcs->glDeleteFramebuffers(1, &frameBufferId);
+}
+
+void GraphicsHelperGL3_3::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode)
+{
+ switch (mode) {
+ case FBODraw:
+ m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBORead:
+ m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBOReadAndDraw:
+ default:
+ m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId);
+ return;
+ }
+}
+
+GLuint GraphicsHelperGL3_3::boundFrameBufferObject()
+{
+ GLint id = 0;
+ m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id);
+ return id;
+}
+
+bool GraphicsHelperGL3_3::checkFrameBufferComplete()
+{
+ return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+}
+
+bool GraphicsHelperGL3_3::frameBufferNeedsRenderBuffer(const Attachment &attachment)
+{
+ Q_UNUSED(attachment);
+ return false;
+}
+
+void GraphicsHelperGL3_3::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment)
+{
+ GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT;
+
+ if (attachment.m_point <= QRenderTargetOutput::Color15)
+ attr = GL_COLOR_ATTACHMENT0 + attachment.m_point;
+ else if (attachment.m_point == QRenderTargetOutput::Depth)
+ attr = GL_DEPTH_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::Stencil)
+ attr = GL_STENCIL_ATTACHMENT;
+
+ texture->bind();
+ QOpenGLTexture::Target target = texture->target();
+ if (target == QOpenGLTexture::Target1DArray || target == QOpenGLTexture::Target2DArray ||
+ target == QOpenGLTexture::Target2DMultisampleArray || target == QOpenGLTexture::Target3D)
+ m_funcs->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel, attachment.m_layer);
+ else if (target == QOpenGLTexture::TargetCubeMapArray)
+ m_funcs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel, attachment.m_layer);
+ else if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face != QAbstractTexture::AllFaces)
+ m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel);
+ else
+ m_funcs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel);
+ texture->release();
+}
+
+void GraphicsHelperGL3_3::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment)
+{
+ Q_UNUSED(renderBuffer);
+ Q_UNUSED(attachment);
+ Q_UNREACHABLE();
+}
+
+bool GraphicsHelperGL3_3::supportsFeature(GraphicsHelperInterface::Feature feature) const
+{
+ switch (feature) {
+ case MRT:
+ case UniformBufferObject:
+ case PrimitiveRestart:
+ case RenderBufferDimensionRetrieval:
+ case TextureDimensionRetrieval:
+ case BindableFragmentOutputs:
+ case BlitFramebuffer:
+ return true;
+ case Tessellation:
+ return !m_tessFuncs.isNull();
+ default:
+ return false;
+ }
+}
+
+void GraphicsHelperGL3_3::drawBuffers(GLsizei n, const int *bufs)
+{
+ // Use QVarLengthArray here
+ QVarLengthArray<GLenum, 16> drawBufs(n);
+
+ for (int i = 0; i < n; i++)
+ drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i];
+ m_funcs->glDrawBuffers(n, drawBufs.constData());
+}
+
+void GraphicsHelperGL3_3::bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs)
+{
+ for (auto it = outputs.begin(), end = outputs.end(); it != end; ++it)
+ m_funcs->glBindFragDataLocation(shader, it.value(), it.key().toStdString().c_str());
+}
+
+void GraphicsHelperGL3_3::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+ m_funcs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding);
+}
+
+void GraphicsHelperGL3_3::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding)
+{
+ Q_UNUSED(programId);
+ Q_UNUSED(shaderStorageBlockIndex);
+ Q_UNUSED(shaderStorageBlockBinding);
+ qWarning() << "SSBO are not supported by OpenGL 3.3 (since OpenGL 4.3)";
+}
+
+void GraphicsHelperGL3_3::bindBufferBase(GLenum target, GLuint index, GLuint buffer)
+{
+ m_funcs->glBindBufferBase(target, index, buffer);
+}
+
+void GraphicsHelperGL3_3::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer)
+{
+ char *bufferData = buffer.data();
+
+ switch (description.m_type) {
+
+ case GL_FLOAT: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_FLOAT_VEC2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_FLOAT_VEC3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_FLOAT_VEC4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3);
+ break;
+ }
+
+ case GL_INT: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_INT_VEC2: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_INT_VEC3: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_INT_VEC4: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_UNSIGNED_INT: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC2: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC3: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC4: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_BOOL: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_BOOL_VEC2: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_BOOL_VEC3: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_BOOL_VEC4: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_2D_RECT:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_2D_RECT:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_1D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: {
+ Q_ASSERT(description.m_size == 1);
+ int value = v.toInt();
+ QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1);
+ break;
+ }
+
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name;
+ break;
+ }
+}
+
+uint GraphicsHelperGL3_3::uniformByteSize(const ShaderUniform &description)
+{
+ uint rawByteSize = 0;
+ int arrayStride = qMax(description.m_arrayStride, 0);
+ int matrixStride = qMax(description.m_matrixStride, 0);
+
+ switch (description.m_type) {
+
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ rawByteSize = 8;
+ break;
+
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ rawByteSize = 12;
+ break;
+
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ rawByteSize = 16;
+ break;
+
+ case GL_FLOAT_MAT2:
+ rawByteSize = matrixStride ? 2 * matrixStride : 16;
+ break;
+
+ case GL_FLOAT_MAT2x4:
+ rawByteSize = matrixStride ? 2 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT4x2:
+ rawByteSize = matrixStride ? 4 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT3:
+ rawByteSize = matrixStride ? 3 * matrixStride : 36;
+ break;
+
+ case GL_FLOAT_MAT2x3:
+ rawByteSize = matrixStride ? 2 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT3x2:
+ rawByteSize = matrixStride ? 3 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT4:
+ rawByteSize = matrixStride ? 4 * matrixStride : 64;
+ break;
+
+ case GL_FLOAT_MAT4x3:
+ rawByteSize = matrixStride ? 4 * matrixStride : 48;
+ break;
+
+ case GL_FLOAT_MAT3x4:
+ rawByteSize = matrixStride ? 3 * matrixStride : 48;
+ break;
+
+ case GL_BOOL:
+ rawByteSize = 1;
+ break;
+
+ case GL_BOOL_VEC2:
+ rawByteSize = 2;
+ break;
+
+ case GL_BOOL_VEC3:
+ rawByteSize = 3;
+ break;
+
+ case GL_BOOL_VEC4:
+ rawByteSize = 4;
+ break;
+
+ case GL_INT:
+ case GL_FLOAT:
+ case GL_UNSIGNED_INT:
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_2D_RECT:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_2D_RECT:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_1D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ rawByteSize = 4;
+ break;
+ }
+
+ return arrayStride ? rawByteSize * arrayStride : rawByteSize;
+}
+
+void GraphicsHelperGL3_3::enableClipPlane(int clipPlane)
+{
+ m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL3_3::disableClipPlane(int clipPlane)
+{
+ m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL3_3::setClipPlane(int clipPlane, const QVector3D &normal, float distance)
+{
+ // deprecated
+ Q_UNUSED(clipPlane);
+ Q_UNUSED(normal);
+ Q_UNUSED(distance);
+}
+
+GLint GraphicsHelperGL3_3::maxClipPlaneCount()
+{
+ GLint max = 0;
+ m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max);
+ return max;
+}
+
+void GraphicsHelperGL3_3::memoryBarrier(QMemoryBarrier::Operations barriers)
+{
+ Q_UNUSED(barriers);
+ qWarning() << "memory barrier is not supported by OpenGL 3.3 (since 4.3)";
+}
+
+void GraphicsHelperGL3_3::enablePrimitiveRestart(int primitiveRestartIndex)
+{
+ m_funcs->glPrimitiveRestartIndex(primitiveRestartIndex);
+ m_funcs->glEnable(GL_PRIMITIVE_RESTART);
+}
+
+void GraphicsHelperGL3_3::enableVertexAttributeArray(int location)
+{
+ m_funcs->glEnableVertexAttribArray(location);
+}
+
+void GraphicsHelperGL3_3::disablePrimitiveRestart()
+{
+ m_funcs->glDisable(GL_PRIMITIVE_RESTART);
+}
+
+void GraphicsHelperGL3_3::clearBufferf(GLint drawbuffer, const QVector4D &values)
+{
+ GLfloat vec[4] = {values[0], values[1], values[2], values[3]};
+ m_funcs->glClearBufferfv(GL_COLOR, drawbuffer, vec);
+}
+
+void GraphicsHelperGL3_3::pointSize(bool programmable, GLfloat value)
+{
+ if (programmable) {
+ m_funcs->glEnable(GL_PROGRAM_POINT_SIZE);
+ } else {
+ m_funcs->glDisable(GL_PROGRAM_POINT_SIZE);
+ m_funcs->glPointSize(value);
+ }
+}
+
+void GraphicsHelperGL3_3::enablei(GLenum cap, GLuint index)
+{
+ m_funcs->glEnablei(cap, index);
+}
+
+void GraphicsHelperGL3_3::disablei(GLenum cap, GLuint index)
+{
+ m_funcs->glDisablei(cap, index);
+}
+
+void GraphicsHelperGL3_3::setSeamlessCubemap(bool enable)
+{
+ if (enable)
+ m_funcs->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ else
+ m_funcs->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+}
+
+QSize GraphicsHelperGL3_3::getRenderBufferDimensions(GLuint renderBufferId)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+ return QSize(width, height);
+}
+
+QSize GraphicsHelperGL3_3::getTextureDimensions(GLuint textureId, GLenum target, uint level)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindTexture(target, textureId);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
+ m_funcs->glBindTexture(target, 0);
+
+ return QSize(width, height);
+}
+
+void GraphicsHelperGL3_3::dispatchCompute(GLuint wx, GLuint wy, GLuint wz)
+{
+ Q_UNUSED(wx);
+ Q_UNUSED(wy);
+ Q_UNUSED(wz);
+ qWarning() << "Compute Shaders are not supported by OpenGL 3.3 (since OpenGL 4.3)";
+}
+
+char *GraphicsHelperGL3_3::mapBuffer(GLenum target, GLsizeiptr size)
+{
+ return static_cast<char*>(m_funcs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
+}
+
+GLboolean GraphicsHelperGL3_3::unmapBuffer(GLenum target)
+{
+ return m_funcs->glUnmapBuffer(target);
+}
+
+void GraphicsHelperGL3_3::glUniform1fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform1fv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform2fv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform3fv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform4fv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform1iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform1iv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform2iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform2iv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform3iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform3iv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform4iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform4iv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform1uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform1uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform2uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform2uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform3uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform3uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniform4uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform4uiv(location, count, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2x3fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3x2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2x4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4x2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3x4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL3_3::glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4x3fv(location, count, false, values);
+}
+
+UniformType GraphicsHelperGL3_3::uniformTypeFromGLType(GLenum type)
+{
+ switch (type) {
+ case GL_FLOAT:
+ return UniformType::Float;
+ case GL_FLOAT_VEC2:
+ return UniformType::Vec2;
+ case GL_FLOAT_VEC3:
+ return UniformType::Vec3;
+ case GL_FLOAT_VEC4:
+ return UniformType::Vec4;
+ case GL_FLOAT_MAT2:
+ return UniformType::Mat2;
+ case GL_FLOAT_MAT3:
+ return UniformType::Mat3;
+ case GL_FLOAT_MAT4:
+ return UniformType::Mat4;
+ case GL_FLOAT_MAT2x3:
+ return UniformType::Mat2x3;
+ case GL_FLOAT_MAT3x2:
+ return UniformType::Mat3x2;
+ case GL_FLOAT_MAT2x4:
+ return UniformType::Mat2x4;
+ case GL_FLOAT_MAT4x2:
+ return UniformType::Mat4x2;
+ case GL_FLOAT_MAT3x4:
+ return UniformType::Mat3x4;
+ case GL_FLOAT_MAT4x3:
+ return UniformType::Mat4x3;
+ case GL_INT:
+ return UniformType::Int;
+ case GL_INT_VEC2:
+ return UniformType::IVec2;
+ case GL_INT_VEC3:
+ return UniformType::IVec3;
+ case GL_INT_VEC4:
+ return UniformType::IVec4;
+ case GL_UNSIGNED_INT:
+ return UniformType::UInt;
+ case GL_UNSIGNED_INT_VEC2:
+ return UniformType::UIVec2;
+ case GL_UNSIGNED_INT_VEC3:
+ return UniformType::UIVec3;
+ case GL_UNSIGNED_INT_VEC4:
+ return UniformType::UIVec4;
+ case GL_BOOL:
+ return UniformType::Bool;
+ case GL_BOOL_VEC2:
+ return UniformType::BVec2;
+ case GL_BOOL_VEC3:
+ return UniformType::BVec3;
+ case GL_BOOL_VEC4:
+ return UniformType::BVec4;
+
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_2D_RECT:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_SAMPLER_3D:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ return UniformType::Sampler;
+ default:
+ Q_UNREACHABLE();
+ return UniformType::Float;
+ }
+}
+
+void GraphicsHelperGL3_3::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+ m_funcs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h
new file mode 100644
index 000000000..0ecdd3620
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL3_3_P_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERGL3_3_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/graphicshelperinterface_p.h>
+#include <QtCore/qscopedpointer.h>
+
+#ifndef QT_OPENGL_ES_2
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLFunctions_3_3_Core;
+class QOpenGLExtension_ARB_tessellation_shader;
+
+namespace Qt3DRender {
+namespace Render {
+
+class Q_AUTOTEST_EXPORT GraphicsHelperGL3_3 : public GraphicsHelperInterface
+{
+public:
+ GraphicsHelperGL3_3();
+ ~GraphicsHelperGL3_3();
+
+ // QGraphicHelperInterface interface
+ void alphaTest(GLenum mode1, GLenum mode2) override;
+ void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+ void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override;
+ bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override;
+ void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override;
+ void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override;
+ void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override;
+ void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override;
+ void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+ void blendEquation(GLenum mode) override;
+ void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override;
+ void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override;
+ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override;
+ GLuint boundFrameBufferObject() override;
+ void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override;
+ bool checkFrameBufferComplete() override;
+ void clearBufferf(GLint drawbuffer, const QVector4D &values) override;
+ GLuint createFrameBufferObject() override;
+ void depthMask(GLenum mode) override;
+ void depthTest(GLenum mode) override;
+ void disableClipPlane(int clipPlane) override;
+ void disablei(GLenum cap, GLuint index) override;
+ void disablePrimitiveRestart() override;
+ void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override;
+ char *mapBuffer(GLenum target, GLsizeiptr size) override;
+ GLboolean unmapBuffer(GLenum target) override;
+ void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override;
+ void drawArraysIndirect(GLenum mode,void *indirect) override;
+ void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override;
+ void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override;
+ void drawBuffers(GLsizei n, const int *bufs) override;
+ void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override;
+ void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override;
+ void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override;
+ void enableClipPlane(int clipPlane) override;
+ void enablei(GLenum cap, GLuint index) override;
+ void enablePrimitiveRestart(int primitiveRestartIndex) override;
+ void enableVertexAttributeArray(int location) override;
+ void frontFace(GLenum mode) override;
+ QSize getRenderBufferDimensions(GLuint renderBufferId) override;
+ QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override;
+ void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override;
+ void pointSize(bool programmable, GLfloat value) override;
+ GLint maxClipPlaneCount() override;
+ void memoryBarrier(QMemoryBarrier::Operations barriers) override;
+ QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override;
+ QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override;
+ QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override;
+ QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override;
+ void releaseFrameBufferObject(GLuint frameBufferId) override;
+ void setMSAAEnabled(bool enable) override;
+ void setAlphaCoverageEnabled(bool enable) override;
+ void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override;
+ void setSeamlessCubemap(bool enable) override;
+ void setVerticesPerPatch(GLint verticesPerPatch) override;
+ bool supportsFeature(Feature feature) const override;
+ uint uniformByteSize(const ShaderUniform &description) override;
+ void useProgram(GLuint programId) override;
+ void vertexAttribDivisor(GLuint index, GLuint divisor) override;
+ void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override;
+ void readBuffer(GLenum mode) override;
+ void drawBuffer(GLenum mode) override;
+
+ void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ void glUniform1iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform2iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform3iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform4iv(GLint location, GLsizei count, const GLint *value) override;
+
+ void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override;
+
+ void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ UniformType uniformTypeFromGLType(GLenum glType) override;
+
+private:
+ QOpenGLFunctions_3_3_Core *m_funcs;
+ QScopedPointer<QOpenGLExtension_ARB_tessellation_shader> m_tessFuncs;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL3_3_P_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp
new file mode 100644
index 000000000..ce1b8ac2b
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp
@@ -0,0 +1,1242 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "graphicshelpergl4_p.h"
+
+#ifndef QT_OPENGL_ES_2
+#include <QOpenGLFunctions_4_3_Core>
+#include <QtOpenGLExtensions/qopenglextensions.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <private/attachmentpack_p.h>
+#include <private/qgraphicsutils_p.h>
+
+# ifndef QT_OPENGL_4_3
+# ifndef GL_PATCH_VERTICES
+# define GL_PATCH_VERTICES 36466
+# endif
+# define GL_ACTIVE_RESOURCES 0x92F5
+# define GL_BUFFER_BINDING 0x9302
+# define GL_BUFFER_DATA_SIZE 0x9303
+# define GL_NUM_ACTIVE_VARIABLES 0x9304
+# define GL_SHADER_STORAGE_BLOCK 0x92E6
+# define GL_UNIFORM 0x92E1
+# define GL_UNIFORM_BLOCK 0x92E2
+# define GL_UNIFORM_BLOCK_INDEX 0x8A3A
+# define GL_UNIFORM_OFFSET 0x8A3B
+# define GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+# define GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+# define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+# define GL_UNIFORM_BLOCK_BINDING 0x8A3F
+# define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+# define GL_ALL_BARRIER_BITS 0xFFFFFFFF
+# define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001
+# define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002
+# define GL_UNIFORM_BARRIER_BIT 0x00000004
+# define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008
+# define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
+# define GL_COMMAND_BARRIER_BIT 0x00000040
+# define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080
+# define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100
+# define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200
+# define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400
+# define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800
+# define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000
+# define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
+# define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000
+# endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+namespace {
+
+GLbitfield memoryBarrierGLBitfield(QMemoryBarrier::Operations barriers)
+{
+ GLbitfield bits = 0;
+
+ if (barriers.testFlag(QMemoryBarrier::All)) {
+ bits |= GL_ALL_BARRIER_BITS;
+ return bits;
+ }
+
+ if (barriers.testFlag(QMemoryBarrier::VertexAttributeArray))
+ bits |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::ElementArray))
+ bits |= GL_ELEMENT_ARRAY_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::Uniform))
+ bits |= GL_UNIFORM_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::TextureFetch))
+ bits |= GL_TEXTURE_FETCH_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::ShaderImageAccess))
+ bits |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::Command))
+ bits |= GL_COMMAND_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::PixelBuffer))
+ bits |= GL_PIXEL_BUFFER_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::TextureUpdate))
+ bits |= GL_TEXTURE_UPDATE_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::BufferUpdate))
+ bits |= GL_BUFFER_UPDATE_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::FrameBuffer))
+ bits |= GL_FRAMEBUFFER_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::TransformFeedback))
+ bits |= GL_TRANSFORM_FEEDBACK_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::AtomicCounter))
+ bits |= GL_ATOMIC_COUNTER_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::ShaderStorage))
+ bits |= GL_SHADER_STORAGE_BARRIER_BIT;
+ if (barriers.testFlag(QMemoryBarrier::QueryBuffer))
+ bits |= GL_QUERY_BUFFER_BARRIER_BIT;
+
+ return bits;
+}
+
+}
+
+GraphicsHelperGL4::GraphicsHelperGL4()
+ : m_funcs(nullptr)
+{
+}
+
+void GraphicsHelperGL4::initializeHelper(QOpenGLContext *context,
+ QAbstractOpenGLFunctions *functions)
+{
+ Q_UNUSED(context);
+ m_funcs = static_cast<QOpenGLFunctions_4_3_Core*>(functions);
+ const bool ok = m_funcs->initializeOpenGLFunctions();
+ Q_ASSERT(ok);
+ Q_UNUSED(ok);
+}
+
+void GraphicsHelperGL4::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLsizei instances,
+ GLint baseVertex,
+ GLint baseInstance)
+{
+ if (baseInstance != 0)
+ qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2";
+
+ // glDrawElements OpenGL 3.1 or greater
+ m_funcs->glDrawElementsInstancedBaseVertex(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ instances,
+ baseVertex);
+}
+
+void GraphicsHelperGL4::drawArraysInstanced(GLenum primitiveType,
+ GLint first,
+ GLsizei count,
+ GLsizei instances)
+{
+ // glDrawArraysInstanced OpenGL 3.1 or greater
+ m_funcs->glDrawArraysInstanced(primitiveType,
+ first,
+ count,
+ instances);
+}
+
+void GraphicsHelperGL4::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance)
+{
+ m_funcs->glDrawArraysInstancedBaseInstance(primitiveType,
+ first,
+ count,
+ instances,
+ baseInstance);
+}
+
+void GraphicsHelperGL4::drawElements(GLenum primitiveType,
+ GLsizei primitiveCount,
+ GLint indexType,
+ void *indices,
+ GLint baseVertex)
+{
+ m_funcs->glDrawElementsBaseVertex(primitiveType,
+ primitiveCount,
+ indexType,
+ indices,
+ baseVertex);
+}
+
+void GraphicsHelperGL4::drawElementsIndirect(GLenum mode,
+ GLenum type,
+ void *indirect)
+{
+ m_funcs->glDrawElementsIndirect(mode, type, indirect);
+}
+
+void GraphicsHelperGL4::drawArrays(GLenum primitiveType,
+ GLint first,
+ GLsizei count)
+{
+ m_funcs->glDrawArrays(primitiveType,
+ first,
+ count);
+}
+
+void GraphicsHelperGL4::drawArraysIndirect(GLenum mode, void *indirect)
+{
+ m_funcs->glDrawArraysIndirect(mode, indirect);
+}
+
+void GraphicsHelperGL4::setVerticesPerPatch(GLint verticesPerPatch)
+{
+ m_funcs->glPatchParameteri(GL_PATCH_VERTICES, verticesPerPatch);
+}
+
+void GraphicsHelperGL4::useProgram(GLuint programId)
+{
+ m_funcs->glUseProgram(programId);
+}
+
+QVector<ShaderUniform> GraphicsHelperGL4::programUniformsAndLocations(GLuint programId)
+{
+ QVector<ShaderUniform> uniforms;
+
+ GLint nbrActiveUniforms = 0;
+ m_funcs->glGetProgramInterfaceiv(programId, GL_UNIFORM, GL_ACTIVE_RESOURCES, &nbrActiveUniforms);
+ uniforms.reserve(nbrActiveUniforms);
+ char uniformName[256];
+ for (GLint i = 0; i < nbrActiveUniforms; ++i) {
+ ShaderUniform uniform;
+ GLsizei uniformNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength,
+ &uniform.m_size, &uniform.m_type, uniformName);
+ uniformName[sizeof(uniformName) - 1] = '\0';
+ uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName);
+ uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength);
+ // Work around for uniform array names that aren't returned with [0] by some drivers
+ if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]")))
+ uniform.m_name.append(QLatin1String("[0]"));
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride);
+ m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride);
+ uniform.m_rawByteSize = uniformByteSize(uniform);
+ uniforms.append(uniform);
+ qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size
+ << " offset" << uniform.m_offset
+ << " rawSize" << uniform.m_rawByteSize;
+ }
+
+ return uniforms;
+}
+
+QVector<ShaderAttribute> GraphicsHelperGL4::programAttributesAndLocations(GLuint programId)
+{
+ QVector<ShaderAttribute> attributes;
+ GLint nbrActiveAttributes = 0;
+ m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes);
+ attributes.reserve(nbrActiveAttributes);
+ char attributeName[256];
+ for (GLint i = 0; i < nbrActiveAttributes; ++i) {
+ ShaderAttribute attribute;
+ GLsizei attributeNameLength = 0;
+ // Size is 1 for scalar and more for struct or arrays
+ // Type is the GL Type
+ m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength,
+ &attribute.m_size, &attribute.m_type, attributeName);
+ attributeName[sizeof(attributeName) - 1] = '\0';
+ attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName);
+ attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength);
+ attributes.append(attribute);
+ }
+ return attributes;
+}
+
+QVector<ShaderUniformBlock> GraphicsHelperGL4::programUniformBlocks(GLuint programId)
+{
+ QVector<ShaderUniformBlock> blocks;
+ GLint nbrActiveUniformsBlocks = 0;
+ m_funcs->glGetProgramInterfaceiv(programId, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &nbrActiveUniformsBlocks);
+ blocks.reserve(nbrActiveUniformsBlocks);
+ for (GLint i = 0; i < nbrActiveUniformsBlocks; ++i) {
+ QByteArray uniformBlockName(256, '\0');
+ GLsizei length = 0;
+ ShaderUniformBlock uniformBlock;
+ m_funcs->glGetProgramResourceName(programId, GL_UNIFORM_BLOCK, i, 256, &length, uniformBlockName.data());
+ uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length));
+ uniformBlock.m_index = i;
+ GLenum prop = GL_BUFFER_BINDING;
+ m_funcs->glGetProgramResourceiv(programId, GL_UNIFORM_BLOCK, i, 1, &prop, 4, NULL, &uniformBlock.m_binding);
+ prop = GL_BUFFER_DATA_SIZE;
+ m_funcs->glGetProgramResourceiv(programId, GL_UNIFORM_BLOCK, i, 1, &prop, 4, NULL, &uniformBlock.m_size);
+ prop = GL_NUM_ACTIVE_VARIABLES;
+ m_funcs->glGetProgramResourceiv(programId, GL_UNIFORM_BLOCK, i, 1, &prop, 4, NULL, &uniformBlock.m_activeUniformsCount);
+ blocks.append(uniformBlock);
+ }
+ return blocks;
+}
+
+QVector<ShaderStorageBlock> GraphicsHelperGL4::programShaderStorageBlocks(GLuint programId)
+{
+ QVector<ShaderStorageBlock> blocks;
+ GLint nbrActiveShaderStorageBlocks = 0;
+ m_funcs->glGetProgramInterfaceiv(programId, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &nbrActiveShaderStorageBlocks);
+ blocks.reserve(nbrActiveShaderStorageBlocks);
+ for (GLint i = 0; i < nbrActiveShaderStorageBlocks; ++i) {
+ QByteArray storageBlockName(256, '\0');
+ GLsizei length = 0;
+ ShaderStorageBlock storageBlock;
+ m_funcs->glGetProgramResourceName(programId, GL_SHADER_STORAGE_BLOCK, i, 256, &length, storageBlockName.data());
+ storageBlock.m_index = i;
+ storageBlock.m_name = QString::fromUtf8(storageBlockName.left(length));
+ GLenum prop = GL_BUFFER_BINDING;
+ m_funcs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_binding);
+ prop = GL_BUFFER_DATA_SIZE;
+ m_funcs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_size);
+ prop = GL_NUM_ACTIVE_VARIABLES;
+ m_funcs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_activeVariablesCount);
+ blocks.push_back(storageBlock);
+ }
+ return blocks;
+}
+
+void GraphicsHelperGL4::vertexAttribDivisor(GLuint index, GLuint divisor)
+{
+ m_funcs->glVertexAttribDivisor(index, divisor);
+}
+
+void GraphicsHelperGL4::vertexAttributePointer(GLenum shaderDataType,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer)
+{
+ switch (shaderDataType) {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_MAT4:
+ m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ break;
+
+ case GL_INT:
+ case GL_INT_VEC2:
+ case GL_INT_VEC3:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC4:
+ m_funcs->glVertexAttribIPointer(index, size, type, stride, pointer);
+ break;
+
+ case GL_DOUBLE:
+ case GL_DOUBLE_VEC2:
+ case GL_DOUBLE_VEC3:
+ case GL_DOUBLE_VEC4:
+ m_funcs->glVertexAttribLPointer(index, size, type, stride, pointer);
+ break;
+
+ default:
+ qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type";
+ Q_UNREACHABLE();
+ }
+}
+
+void GraphicsHelperGL4::readBuffer(GLenum mode)
+{
+ m_funcs->glReadBuffer(mode);
+}
+
+void GraphicsHelperGL4::drawBuffer(GLenum mode)
+{
+ m_funcs->glDrawBuffer(mode);
+}
+
+void GraphicsHelperGL4::glUniform1fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform1fv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform2fv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform3fv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniform4fv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform1iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform1iv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform2iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform2iv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform3iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform3iv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform4iv(GLint location, GLsizei count, const GLint *values)
+{
+ m_funcs->glUniform4iv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform1uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform1uiv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform2uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform2uiv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform3uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform3uiv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniform4uiv(GLint location, GLsizei count, const GLuint *values)
+{
+ m_funcs->glUniform4uiv(location, count, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2x3fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3x2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix2x4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4x2fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix3x4fv(location, count, false, values);
+}
+
+void GraphicsHelperGL4::glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *values)
+{
+ m_funcs->glUniformMatrix4x3fv(location, count, false, values);
+}
+
+UniformType GraphicsHelperGL4::uniformTypeFromGLType(GLenum type)
+{
+ switch (type) {
+ case GL_FLOAT:
+ return UniformType::Float;
+ case GL_FLOAT_VEC2:
+ return UniformType::Vec2;
+ case GL_FLOAT_VEC3:
+ return UniformType::Vec3;
+ case GL_FLOAT_VEC4:
+ return UniformType::Vec4;
+ case GL_FLOAT_MAT2:
+ return UniformType::Mat2;
+ case GL_FLOAT_MAT3:
+ return UniformType::Mat3;
+ case GL_FLOAT_MAT4:
+ return UniformType::Mat4;
+ case GL_FLOAT_MAT2x3:
+ return UniformType::Mat2x3;
+ case GL_FLOAT_MAT3x2:
+ return UniformType::Mat3x2;
+ case GL_FLOAT_MAT2x4:
+ return UniformType::Mat2x4;
+ case GL_FLOAT_MAT4x2:
+ return UniformType::Mat4x2;
+ case GL_FLOAT_MAT3x4:
+ return UniformType::Mat3x4;
+ case GL_FLOAT_MAT4x3:
+ return UniformType::Mat4x3;
+ case GL_INT:
+ return UniformType::Int;
+ case GL_INT_VEC2:
+ return UniformType::IVec2;
+ case GL_INT_VEC3:
+ return UniformType::IVec3;
+ case GL_INT_VEC4:
+ return UniformType::IVec4;
+ case GL_UNSIGNED_INT:
+ return UniformType::UInt;
+ case GL_UNSIGNED_INT_VEC2:
+ return UniformType::UIVec2;
+ case GL_UNSIGNED_INT_VEC3:
+ return UniformType::UIVec3;
+ case GL_UNSIGNED_INT_VEC4:
+ return UniformType::UIVec4;
+ case GL_BOOL:
+ return UniformType::Bool;
+ case GL_BOOL_VEC2:
+ return UniformType::BVec2;
+ case GL_BOOL_VEC3:
+ return UniformType::BVec3;
+ case GL_BOOL_VEC4:
+ return UniformType::BVec4;
+
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_2D_RECT:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_CUBE_MAP_ARRAY:
+ case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_CUBE_MAP_ARRAY:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY:
+ return UniformType::Sampler;
+ default:
+ // TO DO: Add support for Doubles and Images
+ Q_UNREACHABLE();
+ return UniformType::Float;
+ }
+}
+
+void GraphicsHelperGL4::blendEquation(GLenum mode)
+{
+ m_funcs->glBlendEquation(mode);
+}
+
+void GraphicsHelperGL4::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor)
+{
+ m_funcs->glBlendFunci(buf, sfactor, dfactor);
+}
+
+void GraphicsHelperGL4::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha)
+{
+ m_funcs->glBlendFuncSeparatei(buf, sRGB, dRGB, sAlpha, dAlpha);
+}
+
+void GraphicsHelperGL4::alphaTest(GLenum, GLenum)
+{
+ qCWarning(Render::Rendering) << "AlphaTest not available with OpenGL 3.2 core";
+}
+
+void GraphicsHelperGL4::depthTest(GLenum mode)
+{
+ m_funcs->glEnable(GL_DEPTH_TEST);
+ m_funcs->glDepthFunc(mode);
+}
+
+void GraphicsHelperGL4::depthMask(GLenum mode)
+{
+ m_funcs->glDepthMask(mode);
+}
+
+void GraphicsHelperGL4::frontFace(GLenum mode)
+{
+ m_funcs->glFrontFace(mode);
+
+}
+
+void GraphicsHelperGL4::setMSAAEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_MULTISAMPLE)
+ : m_funcs->glDisable(GL_MULTISAMPLE);
+}
+
+void GraphicsHelperGL4::setAlphaCoverageEnabled(bool enabled)
+{
+ enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)
+ : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+}
+
+GLuint GraphicsHelperGL4::createFrameBufferObject()
+{
+ GLuint id;
+ m_funcs->glGenFramebuffers(1, &id);
+ return id;
+}
+
+void GraphicsHelperGL4::releaseFrameBufferObject(GLuint frameBufferId)
+{
+ m_funcs->glDeleteFramebuffers(1, &frameBufferId);
+}
+
+void GraphicsHelperGL4::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode)
+{
+ switch (mode) {
+ case FBODraw:
+ m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBORead:
+ m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId);
+ return;
+ case FBOReadAndDraw:
+ default:
+ m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId);
+ return;
+ }
+}
+
+GLuint GraphicsHelperGL4::boundFrameBufferObject()
+{
+ GLint id = 0;
+ m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id);
+ return id;
+}
+
+bool GraphicsHelperGL4::checkFrameBufferComplete()
+{
+ return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+}
+
+bool GraphicsHelperGL4::frameBufferNeedsRenderBuffer(const Attachment &attachment)
+{
+ Q_UNUSED(attachment);
+ return false;
+}
+
+void GraphicsHelperGL4::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment)
+{
+ GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT;
+
+ if (attachment.m_point <= QRenderTargetOutput::Color15)
+ attr = GL_COLOR_ATTACHMENT0 + attachment.m_point;
+ else if (attachment.m_point == QRenderTargetOutput::Depth)
+ attr = GL_DEPTH_ATTACHMENT;
+ else if (attachment.m_point == QRenderTargetOutput::Stencil)
+ attr = GL_STENCIL_ATTACHMENT;
+
+ texture->bind();
+ QOpenGLTexture::Target target = texture->target();
+ if (target == QOpenGLTexture::Target1DArray || target == QOpenGLTexture::Target2DArray ||
+ target == QOpenGLTexture::Target2DMultisampleArray || target == QOpenGLTexture::Target3D)
+ m_funcs->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel, attachment.m_layer);
+ else if (target == QOpenGLTexture::TargetCubeMapArray)
+ m_funcs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel, attachment.m_layer);
+ else if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face != QAbstractTexture::AllFaces)
+ m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel);
+ else
+ m_funcs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel);
+ texture->release();
+}
+
+void GraphicsHelperGL4::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment)
+{
+ Q_UNUSED(renderBuffer);
+ Q_UNUSED(attachment);
+ Q_UNREACHABLE();
+}
+
+bool GraphicsHelperGL4::supportsFeature(GraphicsHelperInterface::Feature feature) const
+{
+ switch (feature) {
+ case MRT:
+ case Tessellation:
+ case UniformBufferObject:
+ case BindableFragmentOutputs:
+ case PrimitiveRestart:
+ case RenderBufferDimensionRetrieval:
+ case TextureDimensionRetrieval:
+ case ShaderStorageObject:
+ case Compute:
+ case DrawBuffersBlend:
+ case BlitFramebuffer:
+ case IndirectDrawing:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void GraphicsHelperGL4::drawBuffers(GLsizei n, const int *bufs)
+{
+ // Use QVarLengthArray here
+ QVarLengthArray<GLenum, 16> drawBufs(n);
+
+ for (int i = 0; i < n; i++)
+ drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i];
+ m_funcs->glDrawBuffers(n, drawBufs.constData());
+}
+
+void GraphicsHelperGL4::bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs)
+{
+ for (auto it = outputs.begin(), end = outputs.end(); it != end; ++it)
+ m_funcs->glBindFragDataLocation(shader, it.value(), it.key().toStdString().c_str());
+}
+
+void GraphicsHelperGL4::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+{
+ m_funcs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding);
+}
+
+void GraphicsHelperGL4::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding)
+{
+ m_funcs->glShaderStorageBlockBinding(programId, shaderStorageBlockIndex, shaderStorageBlockBinding);
+}
+
+void GraphicsHelperGL4::bindBufferBase(GLenum target, GLuint index, GLuint buffer)
+{
+ m_funcs->glBindBufferBase(target, index, buffer);
+}
+
+void GraphicsHelperGL4::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer)
+{
+ char *bufferData = buffer.data();
+
+ switch (description.m_type) {
+
+ case GL_FLOAT: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_FLOAT_VEC2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_FLOAT_VEC3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_FLOAT_VEC4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT2x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT3x4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x2: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2);
+ break;
+ }
+
+ case GL_FLOAT_MAT4x3: {
+ const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12);
+ QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3);
+ break;
+ }
+
+ case GL_INT: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_INT_VEC2: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_INT_VEC3: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_INT_VEC4: {
+ const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_UNSIGNED_INT: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC2: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC3: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_UNSIGNED_INT_VEC4: {
+ const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_BOOL: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 1);
+ break;
+ }
+
+ case GL_BOOL_VEC2: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 2);
+ break;
+ }
+
+ case GL_BOOL_VEC3: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 3);
+ break;
+ }
+
+ case GL_BOOL_VEC4: {
+ const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4);
+ QGraphicsUtils::fillDataArray(bufferData, data, description, 4);
+ break;
+ }
+
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_2D_RECT:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_2D_RECT:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_1D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: {
+ Q_ASSERT(description.m_size == 1);
+ int value = v.toInt();
+ QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1);
+ break;
+ }
+
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name;
+ break;
+ }
+}
+
+uint GraphicsHelperGL4::uniformByteSize(const ShaderUniform &description)
+{
+ uint rawByteSize = 0;
+ int arrayStride = qMax(description.m_arrayStride, 0);
+ int matrixStride = qMax(description.m_matrixStride, 0);
+
+ switch (description.m_type) {
+
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ rawByteSize = 8;
+ break;
+
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ rawByteSize = 12;
+ break;
+
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ rawByteSize = 16;
+ break;
+
+ case GL_FLOAT_MAT2:
+ rawByteSize = matrixStride ? 2 * matrixStride : 16;
+ break;
+
+ case GL_FLOAT_MAT2x4:
+ rawByteSize = matrixStride ? 2 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT4x2:
+ rawByteSize = matrixStride ? 4 * matrixStride : 32;
+ break;
+
+ case GL_FLOAT_MAT3:
+ rawByteSize = matrixStride ? 3 * matrixStride : 36;
+ break;
+
+ case GL_FLOAT_MAT2x3:
+ rawByteSize = matrixStride ? 2 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT3x2:
+ rawByteSize = matrixStride ? 3 * matrixStride : 24;
+ break;
+
+ case GL_FLOAT_MAT4:
+ rawByteSize = matrixStride ? 4 * matrixStride : 64;
+ break;
+
+ case GL_FLOAT_MAT4x3:
+ rawByteSize = matrixStride ? 4 * matrixStride : 48;
+ break;
+
+ case GL_FLOAT_MAT3x4:
+ rawByteSize = matrixStride ? 3 * matrixStride : 48;
+ break;
+
+ case GL_BOOL:
+ rawByteSize = 1;
+ break;
+
+ case GL_BOOL_VEC2:
+ rawByteSize = 2;
+ break;
+
+ case GL_BOOL_VEC3:
+ rawByteSize = 3;
+ break;
+
+ case GL_BOOL_VEC4:
+ rawByteSize = 4;
+ break;
+
+ case GL_INT:
+ case GL_FLOAT:
+ case GL_UNSIGNED_INT:
+ case GL_SAMPLER_1D:
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_BUFFER:
+ case GL_SAMPLER_2D_RECT:
+ case GL_INT_SAMPLER_1D:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_BUFFER:
+ case GL_INT_SAMPLER_2D_RECT:
+ case GL_UNSIGNED_INT_SAMPLER_1D:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+ case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
+ case GL_SAMPLER_1D_SHADOW:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_SAMPLER_1D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_1D_ARRAY:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_1D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_RECT_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+ rawByteSize = 4;
+ break;
+ }
+
+ return arrayStride ? rawByteSize * arrayStride : rawByteSize;
+}
+
+void GraphicsHelperGL4::enableClipPlane(int clipPlane)
+{
+ m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL4::disableClipPlane(int clipPlane)
+{
+ m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane);
+}
+
+void GraphicsHelperGL4::setClipPlane(int, const QVector3D &, float)
+{
+ // deprecated
+}
+
+GLint GraphicsHelperGL4::maxClipPlaneCount()
+{
+ GLint max = 0;
+ m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max);
+ return max;
+}
+
+void GraphicsHelperGL4::memoryBarrier(QMemoryBarrier::Operations barriers)
+{
+ m_funcs->glMemoryBarrier(memoryBarrierGLBitfield(barriers));
+}
+
+void GraphicsHelperGL4::enablePrimitiveRestart(int primitiveRestartIndex)
+{
+ m_funcs->glPrimitiveRestartIndex(primitiveRestartIndex);
+ m_funcs->glEnable(GL_PRIMITIVE_RESTART);
+}
+
+void GraphicsHelperGL4::enableVertexAttributeArray(int location)
+{
+ m_funcs->glEnableVertexAttribArray(location);
+}
+
+void GraphicsHelperGL4::disablePrimitiveRestart()
+{
+ m_funcs->glDisable(GL_PRIMITIVE_RESTART);
+}
+
+void GraphicsHelperGL4::clearBufferf(GLint drawbuffer, const QVector4D &values)
+{
+ GLfloat vec[4] = {values[0], values[1], values[2], values[3]};
+ m_funcs->glClearBufferfv(GL_COLOR, drawbuffer, vec);
+}
+
+void GraphicsHelperGL4::pointSize(bool programmable, GLfloat value)
+{
+ if (programmable) {
+ m_funcs->glEnable(GL_PROGRAM_POINT_SIZE);
+ } else {
+ m_funcs->glDisable(GL_PROGRAM_POINT_SIZE);
+ m_funcs->glPointSize(value);
+ }
+}
+
+void GraphicsHelperGL4::enablei(GLenum cap, GLuint index)
+{
+ m_funcs->glEnablei(cap, index);
+}
+
+void GraphicsHelperGL4::disablei(GLenum cap, GLuint index)
+{
+ m_funcs->glDisablei(cap, index);
+}
+
+void GraphicsHelperGL4::setSeamlessCubemap(bool enable)
+{
+ if (enable)
+ m_funcs->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ else
+ m_funcs->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+}
+
+QSize GraphicsHelperGL4::getRenderBufferDimensions(GLuint renderBufferId)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
+ m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
+ m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+ return QSize(width, height);
+}
+
+QSize GraphicsHelperGL4::getTextureDimensions(GLuint textureId, GLenum target, uint level)
+{
+ GLint width = 0;
+ GLint height = 0;
+
+ m_funcs->glBindTexture(target, textureId);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
+ m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
+ m_funcs->glBindTexture(target, 0);
+
+ return QSize(width, height);
+}
+
+void GraphicsHelperGL4::dispatchCompute(GLuint wx, GLuint wy, GLuint wz)
+{
+ m_funcs->glDispatchCompute(wx, wy, wz);
+}
+
+char *GraphicsHelperGL4::mapBuffer(GLenum target, GLsizeiptr size)
+{
+ return static_cast<char*>(m_funcs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
+}
+
+GLboolean GraphicsHelperGL4::unmapBuffer(GLenum target)
+{
+ return m_funcs->glUnmapBuffer(target);
+}
+
+void GraphicsHelperGL4::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+ m_funcs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h
new file mode 100644
index 000000000..3020b16d8
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL4_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERGL4_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/graphicshelperinterface_p.h>
+#include <QtCore/qscopedpointer.h>
+
+#ifndef QT_OPENGL_ES_2
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLFunctions_4_3_Core;
+
+namespace Qt3DRender {
+namespace Render {
+
+class Q_AUTOTEST_EXPORT GraphicsHelperGL4 : public GraphicsHelperInterface
+{
+public:
+ GraphicsHelperGL4();
+
+ // QGraphicHelperInterface interface
+ void alphaTest(GLenum mode1, GLenum mode2) override;
+ void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+ void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override;
+ bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override;
+ void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override;
+ void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override;
+ void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override;
+ void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override;
+ void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override;
+ void blendEquation(GLenum mode) override;
+ void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override;
+ void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override;
+ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override;
+ GLuint boundFrameBufferObject() override;
+ void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override;
+ bool checkFrameBufferComplete() override;
+ void clearBufferf(GLint drawbuffer, const QVector4D &values) override;
+ GLuint createFrameBufferObject() override;
+ void depthMask(GLenum mode) override;
+ void depthTest(GLenum mode) override;
+ void disableClipPlane(int clipPlane) override;
+ void disablei(GLenum cap, GLuint index) override;
+ void disablePrimitiveRestart() override;
+ void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override;
+ char *mapBuffer(GLenum target, GLsizeiptr size) override;
+ GLboolean unmapBuffer(GLenum target) override;
+ void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override;
+ void drawArraysIndirect(GLenum mode,void *indirect) override;
+ void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override;
+ void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override;
+ void drawBuffers(GLsizei n, const int *bufs) override;
+ void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override;
+ void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override;
+ void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override;
+ void enableClipPlane(int clipPlane) override;
+ void enablei(GLenum cap, GLuint index) override;
+ void enablePrimitiveRestart(int primitiveRestartIndex) override;
+ void enableVertexAttributeArray(int location) override;
+ void frontFace(GLenum mode) override;
+ QSize getRenderBufferDimensions(GLuint renderBufferId) override;
+ QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override;
+ void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override;
+ void pointSize(bool programmable, GLfloat value) override;
+ GLint maxClipPlaneCount() override;
+ void memoryBarrier(QMemoryBarrier::Operations barriers) override;
+ QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override;
+ QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override;
+ QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override;
+ QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override;
+ void releaseFrameBufferObject(GLuint frameBufferId) override;
+ void setMSAAEnabled(bool enable) override;
+ void setAlphaCoverageEnabled(bool enable) override;
+ void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override;
+ void setSeamlessCubemap(bool enable) override;
+ void setVerticesPerPatch(GLint verticesPerPatch) override;
+ bool supportsFeature(Feature feature) const override;
+ uint uniformByteSize(const ShaderUniform &description) override;
+ void useProgram(GLuint programId) override;
+ void vertexAttribDivisor(GLuint index, GLuint divisor) override;
+ void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override;
+ void readBuffer(GLenum mode) override;
+ void drawBuffer(GLenum mode) override;
+
+ void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ void glUniform1iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform2iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform3iv(GLint location, GLsizei count, const GLint *value) override;
+ void glUniform4iv(GLint location, GLsizei count, const GLint *value) override;
+
+ void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override;
+ void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override;
+
+ void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override;
+ void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override;
+
+ UniformType uniformTypeFromGLType(GLenum glType) override;
+
+private:
+ QOpenGLFunctions_4_3_Core *m_funcs;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // !QT_OPENGL_ES_2
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL4_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h
new file mode 100644
index 000000000..e41325cb7
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERINTERFACE_H
+#define QT3DRENDER_RENDER_GRAPHICSHELPERINTERFACE_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 <QOpenGLFunctions>
+#include <QOpenGLTexture>
+#include <QVector>
+#include <Qt3DRender/private/shadervariables_p.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <Qt3DRender/qmemorybarrier.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+struct Attachment;
+class RenderBuffer;
+
+class GraphicsHelperInterface
+{
+public:
+ enum Feature {
+ MRT = 0,
+ Tessellation,
+ UniformBufferObject,
+ BindableFragmentOutputs,
+ PrimitiveRestart,
+ RenderBufferDimensionRetrieval,
+ TextureDimensionRetrieval,
+ ShaderStorageObject,
+ Compute,
+ DrawBuffersBlend,
+ BlitFramebuffer,
+ IndirectDrawing,
+ MapBuffer
+ };
+
+ enum FBOBindMode {
+ FBODraw,
+ FBORead,
+ FBOReadAndDraw
+ };
+
+ virtual ~GraphicsHelperInterface() {}
+ virtual void alphaTest(GLenum mode1, GLenum mode2) = 0;
+ virtual void bindBufferBase(GLenum target, GLuint index, GLuint buffer) = 0;
+ virtual void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) = 0;
+ virtual bool frameBufferNeedsRenderBuffer(const Attachment &attachment) = 0;
+ virtual void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) = 0;
+ virtual void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) = 0;
+ virtual void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) = 0;
+ virtual void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) = 0;
+ virtual void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) = 0;
+ virtual void blendEquation(GLenum mode) = 0;
+ virtual void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) = 0;
+ virtual void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) = 0;
+ virtual void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) = 0;
+ virtual GLuint boundFrameBufferObject() = 0;
+ virtual void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) = 0;
+ virtual bool checkFrameBufferComplete() = 0;
+ virtual void clearBufferf(GLint drawbuffer, const QVector4D &values) = 0;
+ virtual GLuint createFrameBufferObject() = 0;
+ virtual void depthMask(GLenum mode) = 0;
+ virtual void depthTest(GLenum mode) = 0;
+ virtual void disableClipPlane(int clipPlane) = 0;
+ virtual void disablei(GLenum cap, GLuint index) = 0;
+ virtual void disablePrimitiveRestart() = 0;
+ virtual void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) = 0;
+ virtual char *mapBuffer(GLenum target, GLsizeiptr size) = 0;
+ virtual GLboolean unmapBuffer(GLenum target) = 0;
+ virtual void drawArrays(GLenum primitiveType, GLint first, GLsizei count) = 0;
+ virtual void drawArraysIndirect(GLenum mode,void *indirect) = 0;
+ virtual void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) = 0;
+ virtual void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseinstance) = 0;
+ virtual void drawBuffers(GLsizei n, const int *bufs) = 0;
+ virtual void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLint baseVertex) = 0;
+ virtual void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) = 0;
+ virtual void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLsizei instances, GLint baseVertex, GLint baseInstance) = 0;
+ virtual void enableClipPlane(int clipPlane) = 0;
+ virtual void enablei(GLenum cap, GLuint index) = 0;
+ virtual void enablePrimitiveRestart(int primitiveRestartIndex) = 0;
+ virtual void enableVertexAttributeArray(int location) = 0;
+ virtual void frontFace(GLenum mode) = 0;
+ virtual QSize getRenderBufferDimensions(GLuint renderBufferId) = 0;
+ virtual QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) = 0;
+ virtual void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) = 0;
+ virtual GLint maxClipPlaneCount() = 0;
+ virtual void memoryBarrier(QMemoryBarrier::Operations barriers) = 0;
+ virtual void pointSize(bool programmable, GLfloat value) = 0;
+ virtual QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) = 0;
+ virtual QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) = 0;
+ virtual QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) = 0;
+ virtual QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) = 0;
+ virtual void releaseFrameBufferObject(GLuint frameBufferId) = 0;
+ virtual void setAlphaCoverageEnabled(bool enable) = 0;
+ virtual void setClipPlane(int clipPlane, const QVector3D &normal, float distance) = 0;
+ virtual void setMSAAEnabled(bool enable) = 0;
+ virtual void setSeamlessCubemap(bool enable) = 0;
+ virtual void setVerticesPerPatch(GLint verticesPerPatch) = 0;
+ virtual bool supportsFeature(Feature feature) const = 0;
+ virtual uint uniformByteSize(const ShaderUniform &description) = 0;
+ virtual void useProgram(GLuint programId) = 0;
+ virtual void vertexAttribDivisor(GLuint index, GLuint divisor) = 0;
+ virtual void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) = 0;
+ virtual void readBuffer(GLenum mode) = 0;
+ virtual void drawBuffer(GLenum mode) = 0;
+
+ virtual void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+
+ virtual void glUniform1iv(GLint location, GLsizei count, const GLint *value) = 0;
+ virtual void glUniform2iv(GLint location, GLsizei count, const GLint *value) = 0;
+ virtual void glUniform3iv(GLint location, GLsizei count, const GLint *value) = 0;
+ virtual void glUniform4iv(GLint location, GLsizei count, const GLint *value) = 0;
+
+ virtual void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) = 0;
+ virtual void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) = 0;
+ virtual void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) = 0;
+ virtual void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) = 0;
+
+ virtual void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+ virtual void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) = 0;
+
+ virtual UniformType uniformTypeFromGLType(GLenum glType) = 0;
+};
+
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GRAPHICSHELPERINTERFACE_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri
new file mode 100644
index 000000000..b3698858a
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri
@@ -0,0 +1,26 @@
+#DEFINES += QT3D_RENDER_ASPECT_OPENGL_DEBUG
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/graphicscontext_p.h \
+ $$PWD/graphicshelperinterface_p.h \
+ $$PWD/graphicshelperes2_p.h \
+ $$PWD/graphicshelperes3_p.h \
+ $$PWD/graphicshelperes3_2_p.h \
+ $$PWD/graphicshelpergl2_p.h \
+ $$PWD/graphicshelpergl3_3_p.h \
+ $$PWD/graphicshelpergl4_p.h \
+ $$PWD/graphicshelpergl3_2_p.h \
+ $$PWD/submissioncontext_p.h
+
+SOURCES += \
+ $$PWD/graphicscontext.cpp \
+ $$PWD/graphicshelperes2.cpp \
+ $$PWD/graphicshelperes3.cpp \
+ $$PWD/graphicshelperes3_2.cpp \
+ $$PWD/graphicshelpergl2.cpp \
+ $$PWD/graphicshelpergl3_3.cpp \
+ $$PWD/graphicshelpergl4.cpp \
+ $$PWD/graphicshelpergl3_2.cpp \
+ $$PWD/submissioncontext.cpp
diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp
new file mode 100644
index 000000000..df7d61902
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp
@@ -0,0 +1,1211 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "submissioncontext_p.h"
+
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/material_p.h>
+#include <Qt3DRender/private/gltexture_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+#include <Qt3DRender/private/attribute_p.h>
+#include <Qt3DRender/private/rendercommand_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <Qt3DRender/private/rendertarget_p.h>
+#include <Qt3DRender/private/graphicshelperinterface_p.h>
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/gltexturemanager_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/qbuffer_p.h>
+#include <Qt3DRender/private/renderbuffer_p.h>
+#include <QOpenGLShaderProgram>
+
+#if !defined(QT_OPENGL_ES_2)
+#include <QOpenGLFunctions_2_0>
+#include <QOpenGLFunctions_3_2_Core>
+#include <QOpenGLFunctions_3_3_Core>
+#include <QOpenGLFunctions_4_3_Core>
+#include <Qt3DRender/private/graphicshelpergl2_p.h>
+#include <Qt3DRender/private/graphicshelpergl3_2_p.h>
+#include <Qt3DRender/private/graphicshelpergl3_3_p.h>
+#include <Qt3DRender/private/graphicshelpergl4_p.h>
+#endif
+#include <Qt3DRender/private/graphicshelperes2_p.h>
+#include <Qt3DRender/private/graphicshelperes3_p.h>
+
+#include <QSurface>
+#include <QWindow>
+#include <QOpenGLTexture>
+#include <QOpenGLDebugLogger>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+namespace Qt3DRender {
+namespace Render {
+
+static QHash<unsigned int, SubmissionContext*> static_contexts;
+
+namespace {
+
+GLBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type)
+{
+ switch (type) {
+ case QAttribute::VertexAttribute:
+ return GLBuffer::ArrayBuffer;
+ case QAttribute::IndexAttribute:
+ return GLBuffer::IndexBuffer;
+ case QAttribute::DrawIndirectAttribute:
+ return GLBuffer::DrawIndirectBuffer;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width, uint height, QAbstractTexture::TextureFormat format)
+{
+ switch (format) {
+ case QAbstractTexture::RGBA32F:
+ {
+ uchar *srcScanline = (uchar *)srcData + stride * (height - 1);
+ for (uint i = 0; i < height; ++i) {
+ uchar *dstScanline = img.scanLine(i);
+ float *pSrc = (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;
+ }
+}
+
+} // anonymous
+
+unsigned int nextFreeContextId()
+{
+ for (unsigned int i=0; i < 0xffff; ++i) {
+ if (!static_contexts.contains(i))
+ return i;
+ }
+
+ qFatal("Couldn't find free context ID");
+ return 0;
+}
+
+SubmissionContext::SubmissionContext()
+ : GraphicsContext()
+ , m_initialized(false)
+ , m_ownCurrent(true)
+ , m_id(nextFreeContextId())
+ , m_surface(nullptr)
+ , m_activeShader(nullptr)
+ , m_activeShaderDNA(0)
+ , m_renderTargetFormat(QAbstractTexture::NoFormat)
+ , m_currClearStencilValue(0)
+ , m_currClearDepthValue(1.f)
+ , m_currClearColorValue(0,0,0,0)
+ , m_material(nullptr)
+ , m_activeFBO(0)
+ , m_boundArrayBuffer(nullptr)
+ , m_stateSet(nullptr)
+ , m_renderer(nullptr)
+ , m_uboTempArray(QByteArray(1024, 0))
+ , m_currentVAO(nullptr)
+{
+ static_contexts[m_id] = this;
+}
+
+SubmissionContext::~SubmissionContext()
+{
+ releaseOpenGL();
+
+ Q_ASSERT(static_contexts[m_id] == this);
+ static_contexts.remove(m_id);
+}
+
+void SubmissionContext::initialize()
+{
+ GraphicsContext::initialize();
+ m_activeTextures.resize(maxTextureUnitsCount());
+}
+
+void SubmissionContext::resolveRenderTargetFormat()
+{
+ 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);
+ Q_ASSERT(m_gl);
+
+ 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();
+
+ // Sets or Create the correct m_glHelper
+ // for the current surface
+ activateGLHelper();
+
+#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
+ GLint err = m_gl->functions()->glGetError();
+ if (err != 0) {
+ qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err;
+ }
+#endif
+
+ if (!m_initialized) {
+ initialize();
+ }
+
+ // need to reset these values every frame, may get overwritten elsewhere
+ m_gl->functions()->glClearColor(m_currClearColorValue.redF(), m_currClearColorValue.greenF(), m_currClearColorValue.blueF(), m_currClearColorValue.alphaF());
+ m_gl->functions()->glClearDepthf(m_currClearDepthValue);
+ m_gl->functions()->glClearStencil(m_currClearStencilValue);
+
+
+ if (m_activeShader) {
+ m_activeShader = nullptr;
+ m_activeShaderDNA = 0;
+ }
+
+ // reset active textures
+ for (int u = 0; u < m_activeTextures.size(); ++u)
+ m_activeTextures[u].texture = nullptr;
+
+ m_boundArrayBuffer = nullptr;
+
+ static int callCount = 0;
+ ++callCount;
+ const int shaderPurgePeriod = 600;
+ if (callCount % shaderPurgePeriod == 0)
+ m_shaderCache->purge();
+
+ return true;
+}
+
+void SubmissionContext::endDrawing(bool swapBuffers)
+{
+ if (swapBuffers)
+ m_gl->swapBuffers(m_surface);
+ if (m_ownCurrent)
+ m_gl->doneCurrent();
+ decayTextureScores();
+}
+
+void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId)
+{
+ 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 {
+ fboId = createRenderTarget(renderTargetNodeId, attachments);
+ }
+ } else {
+ 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)
+{
+ 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)
+{
+ 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?
+ GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager();
+ const QSize s = m_renderTargetsSize[fboId];
+ const auto attachments_ = attachments.attachments();
+ for (const Attachment &attachment : attachments_) {
+ GLTexture *rTex = glTextureManager->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.
+ needsResize |= (rTex != nullptr && rTex->size() != s);
+ if (isActiveRenderTarget) {
+ if (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
+{
+ 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) {
+ int dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio();
+ renderTargetSize *= dpr;
+ }
+ }
+ return renderTargetSize;
+}
+
+QImage SubmissionContext::readFramebuffer(const QRect &rect)
+{
+ 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:
+ // unsupported format
+ Q_UNREACHABLE();
+ return img;
+ }
+
+ GLint samples = 0;
+ m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples);
+ if (samples > 0 && !m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) {
+ qWarning () << 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);
+ qWarning () << 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::setViewport(const QRectF &viewport, const QSize &surfaceSize)
+{
+ // // save for later use; this has nothing to do with the viewport but it is
+ // // here that we get to know the surfaceSize from the RenderView.
+ m_surfaceSize = surfaceSize;
+
+ m_viewport = viewport;
+ QSize size = renderTargetSize(surfaceSize);
+
+ // Check that the returned size is before calling glViewport
+ if (size.isEmpty())
+ return;
+
+ // Qt3D 0------------------> 1 OpenGL 1^
+ // | |
+ // | |
+ // | |
+ // V |
+ // 1 0---------------------> 1
+ // The Viewport is defined between 0 and 1 which allows us to automatically
+ // scale to the size of the provided window surface
+ m_gl->functions()->glViewport(m_viewport.x() * size.width(),
+ (1.0 - m_viewport.y() - m_viewport.height()) * size.height(),
+ m_viewport.width() * size.width(),
+ m_viewport.height() * size.height());
+}
+
+void SubmissionContext::releaseOpenGL()
+{
+ m_shaderCache->clear();
+ m_renderBufferHash.clear();
+
+ // Stop and destroy the OpenGL logger
+ if (m_debugLogger) {
+ m_debugLogger->stopLogging();
+ m_debugLogger.reset(nullptr);
+ }
+}
+
+// The OpenGLContext is not current on any surface at this point
+void SubmissionContext::setOpenGLContext(QOpenGLContext* ctx)
+{
+ Q_ASSERT(ctx && m_shaderCache);
+
+ releaseOpenGL();
+ m_gl = ctx;
+}
+
+void SubmissionContext::activateGLHelper()
+{
+ // Sets the correct GL Helper depending on the surface
+ // If no helper exists, create one
+ m_glHelper = m_glHelpers.value(m_surface);
+ if (!m_glHelper) {
+ m_glHelper = resolveHighestOpenGLFunctions();
+ m_glHelpers.insert(m_surface, m_glHelper);
+ // Note: OpenGLContext is current at this point
+ m_gl->functions()->glDisable(GL_DITHER);
+ }
+}
+
+
+// Called only from RenderThread
+bool SubmissionContext::activateShader(ProgramDNA shaderDNA)
+{
+ if (shaderDNA != m_activeShaderDNA) {
+ // Ensure material uniforms are re-applied
+ m_material = nullptr;
+
+ m_activeShader = m_shaderCache->getShaderProgramForDNA(shaderDNA);
+ if (Q_LIKELY(m_activeShader != nullptr)) {
+ m_activeShader->bind();
+ m_activeShaderDNA = shaderDNA;
+ } else {
+ m_glHelper->useProgram(0);
+ qWarning() << "No shader program found for DNA";
+ m_activeShaderDNA = 0;
+ return false;
+ }
+ }
+ return true;
+}
+
+void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments)
+{
+ // 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;
+ GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager();
+ const auto attachments_ = attachments.attachments();
+ for (const Attachment &attachment : attachments_) {
+ GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid);
+ if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) {
+ QOpenGLTexture *glTex = rTex ? rTex->getOrCreateGLTexture() : nullptr;
+ if (glTex != nullptr) {
+ 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)
+{
+ 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 {
+ qWarning() << "FBO incomplete";
+ }
+}
+
+
+void SubmissionContext::setActiveMaterial(Material *rmat)
+{
+ if (m_material == rmat)
+ return;
+
+ deactivateTexturesWithScope(TextureScopeMaterial);
+ m_material = rmat;
+}
+
+int SubmissionContext::activateTexture(TextureScope scope, GLTexture *tex, int onUnit)
+{
+ // Returns the texture unit to use for the texture
+ // This always return a valid unit, unless there are more textures than
+ // texture unit available for the current material
+ onUnit = assignUnitForTexture(tex);
+
+ // check we didn't overflow the available units
+ if (onUnit == -1)
+ return -1;
+
+ // actually re-bind if required, the tex->dna on the unit not being the same
+ // Note: tex->dna() could be 0 if the texture has not been created yet
+ if (m_activeTextures[onUnit].texture != tex) {
+ QOpenGLTexture *glTex = tex->getOrCreateGLTexture();
+ if (glTex == nullptr)
+ return -1;
+ glTex->bind(onUnit);
+ m_activeTextures[onUnit].texture = tex;
+ }
+
+#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
+ int err = m_gl->functions()->glGetError();
+ if (err)
+ qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16)
+ << tex->textureId() << "on unit" << onUnit;
+#endif
+
+ m_activeTextures[onUnit].score = 200;
+ m_activeTextures[onUnit].pinned = true;
+ m_activeTextures[onUnit].scope = scope;
+
+ return onUnit;
+}
+
+void SubmissionContext::deactivateTexturesWithScope(TextureScope ts)
+{
+ for (int u=0; u<m_activeTextures.size(); ++u) {
+ if (!m_activeTextures[u].pinned)
+ continue; // inactive, ignore
+
+ if (m_activeTextures[u].scope == ts) {
+ m_activeTextures[u].pinned = false;
+ m_activeTextures[u].score = qMax(m_activeTextures[u].score, 1) - 1;
+ }
+ } // of units iteration
+}
+
+void SubmissionContext::deactivateTexture(GLTexture* tex)
+{
+ for (int u=0; u<m_activeTextures.size(); ++u) {
+ if (m_activeTextures[u].texture == tex) {
+ Q_ASSERT(m_activeTextures[u].pinned);
+ m_activeTextures[u].pinned = false;
+ return;
+ }
+ } // of units iteration
+
+ qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex;
+}
+
+/*!
+ \internal
+ Returns a texture unit for a texture, -1 if all texture units are assigned.
+ Tries to use the texture unit with the texture that hasn't been used for the longest time
+ if the texture happens not to be already pinned on a texture unit.
+ */
+GLint SubmissionContext::assignUnitForTexture(GLTexture *tex)
+{
+ int lowestScoredUnit = -1;
+ int lowestScore = 0xfffffff;
+
+ for (int u=0; u<m_activeTextures.size(); ++u) {
+ if (m_activeTextures[u].texture == tex)
+ return u;
+
+ // No texture is currently active on the texture unit
+ // we save the texture unit with the texture that has been on there
+ // the longest time while not being used
+ if (!m_activeTextures[u].pinned) {
+ int score = m_activeTextures[u].score;
+ if (score < lowestScore) {
+ lowestScore = score;
+ lowestScoredUnit = u;
+ }
+ }
+ } // of units iteration
+
+ if (lowestScoredUnit == -1)
+ qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!";
+
+ return lowestScoredUnit;
+}
+
+void SubmissionContext::decayTextureScores()
+{
+ for (int u = 0; u < m_activeTextures.size(); u++)
+ m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0);
+}
+
+void SubmissionContext::setCurrentStateSet(RenderStateSet *ss)
+{
+ if (ss == m_stateSet)
+ return;
+ if (ss)
+ ss->apply(this);
+ m_stateSet = ss;
+}
+
+RenderStateSet *SubmissionContext::currentStateSet() const
+{
+ return m_stateSet;
+}
+
+void SubmissionContext::clearColor(const QColor &color)
+{
+ if (m_currClearColorValue != color) {
+ m_currClearColorValue = color;
+ m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
+ }
+}
+
+void SubmissionContext::clearDepthValue(float depth)
+{
+ if (m_currClearDepthValue != depth) {
+ m_currClearDepthValue = depth;
+ m_gl->functions()->glClearDepthf(depth);
+ }
+}
+
+void SubmissionContext::clearStencilValue(int stencil)
+{
+ if (m_currClearStencilValue != stencil) {
+ m_currClearStencilValue = stencil;
+ m_gl->functions()->glClearStencil(stencil);
+ }
+}
+
+// It will be easier if the QGraphicContext applies the QUniformPack
+// than the other way around
+bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
+{
+ // 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
+ NodeManagers *manager = m_renderer->nodeManagers();
+
+ deactivateTexturesWithScope(TextureScopeMaterial);
+ // Update the uniforms with the correct texture unit id's
+ PackUniformHash &uniformValues = parameterPack.uniforms();
+
+ for (int i = 0; i < parameterPack.textures().size(); ++i) {
+ const ShaderParameterPack::NamedTexture &namedTex = parameterPack.textures().at(i);
+ // Given a Texture QNodeId, we retrieve the associated shared GLTexture
+ if (uniformValues.contains(namedTex.glslNameId)) {
+ GLTexture *t = manager->glTextureManager()->lookupResource(namedTex.texId);
+ if (t != nullptr) {
+ UniformValue &texUniform = uniformValues[namedTex.glslNameId];
+ Q_ASSERT(texUniform.valueType() == UniformValue::TextureValue);
+ const int texUnit = activateTexture(TextureScopeMaterial, t);
+ texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit;
+ if (texUnit == -1)
+ return false;
+ }
+ }
+ }
+
+ QOpenGLShaderProgram *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();
+ int ssboIndex = 0;
+ for (const BlockToSSBO b : blockToSSBOs) {
+ Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
+ GLBuffer *ssbo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::ShaderStorageBuffer);
+ bindShaderStorageBlock(shader->programId(), b.m_blockIndex, ssboIndex);
+ // Needed to avoid conflict where the buffer would already
+ // be bound as a VertexArray
+ bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer);
+ ssbo->bindBufferBase(this, ssboIndex++, GLBuffer::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) {
+ Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
+ GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::UniformBuffer);
+ bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex);
+ // Needed to avoid conflict where the buffer would already
+ // be bound as a VertexArray
+ bindGLBuffer(ubo, GLBuffer::UniformBuffer);
+ ubo->bindBufferBase(this, uboIndex++, GLBuffer::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) {
+ // 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[uniform.m_nameId];
+
+ // skip invalid textures
+ if (v.valueType() == UniformValue::TextureValue && *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::enableAttribute(const VAOVertexAttribute &attr)
+{
+ // Bind buffer within the current VAO
+ GLBuffer *buf = m_renderer->nodeManagers()->glBufferManager()->data(attr.bufferHandle);
+ Q_ASSERT(buf);
+ bindGLBuffer(buf, attr.attributeType);
+
+ // Don't use QOpenGLShaderProgram::setAttributeBuffer() because of QTBUG-43199.
+ // Use the introspection data and set the attribute explicitly
+ m_glHelper->enableVertexAttributeArray(attr.location);
+ m_glHelper->vertexAttributePointer(attr.shaderDataType,
+ attr.location,
+ attr.vertexSize,
+ attr.dataType,
+ GL_TRUE, // TODO: Support normalization property on QAttribute
+ attr.byteStride,
+ reinterpret_cast<const void *>(qintptr(attr.byteOffset)));
+
+
+ // Done by the helper if it supports it
+ if (attr.divisor != 0)
+ m_glHelper->vertexAttribDivisor(attr.location, attr.divisor);
+}
+
+void SubmissionContext::disableAttribute(const SubmissionContext::VAOVertexAttribute &attr)
+{
+ QOpenGLShaderProgram *prog = activeShader();
+ prog->disableAttributeArray(attr.location);
+}
+
+// Note: needs to be called while VAO is bound
+void SubmissionContext::specifyAttribute(const Attribute *attribute,
+ Buffer *buffer,
+ const ShaderAttribute *attributeDescription)
+{
+ const int location = attributeDescription->m_location;
+ if (location < 0) {
+ qCWarning(Backend) << "failed to resolve location for attribute:" << attribute->name();
+ return;
+ }
+
+ const GLint attributeDataType = glDataTypeFromAttributeDataType(attribute->vertexBaseType());
+ const HGLBuffer glBufferHandle = m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId());
+ Q_ASSERT(!glBufferHandle.isNull());
+ const GLBuffer::Type attributeType = attributeTypeToGLBufferType(attribute->attributeType());
+
+ int typeSize = 0;
+ int attrCount = 0;
+
+ if (attribute->vertexSize() >= 1 && attribute->vertexSize() <= 4) {
+ attrCount = 1;
+ } else if (attribute->vertexSize() == 9) {
+ typeSize = byteSizeFromType(attributeDataType);
+ attrCount = 3;
+ } else if (attribute->vertexSize() == 16) {
+ typeSize = byteSizeFromType(attributeDataType);
+ attrCount = 4;
+ } else {
+ Q_UNREACHABLE();
+ }
+
+ for (int i = 0; i < attrCount; i++) {
+ VAOVertexAttribute attr;
+ attr.bufferHandle = glBufferHandle;
+ attr.attributeType = attributeType;
+ attr.location = location + i;
+ attr.dataType = attributeDataType;
+ attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize);
+ attr.vertexSize = attribute->vertexSize() / attrCount;
+ attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize);
+ attr.divisor = attribute->divisor();
+ attr.shaderDataType = attributeDescription->m_type;
+
+ enableAttribute(attr);
+
+ // Save this in the current emulated VAO
+ if (m_currentVAO)
+ m_currentVAO->saveVertexAttribute(attr);
+ }
+}
+
+void SubmissionContext::specifyIndices(Buffer *buffer)
+{
+ GLBuffer *buf = glBufferForRenderBuffer(buffer, GLBuffer::IndexBuffer);
+ if (!bindGLBuffer(buf, GLBuffer::IndexBuffer))
+ qCWarning(Backend) << Q_FUNC_INFO << "binding index buffer failed";
+
+ // bound within the current VAO
+ // Save this in the current emulated VAO
+ if (m_currentVAO)
+ m_currentVAO->saveIndexAttribute(m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()));
+}
+
+void SubmissionContext::updateBuffer(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId());
+ if (it != m_renderBufferHash.end())
+ uploadDataToGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value()));
+}
+
+QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId());
+ if (it != m_renderBufferHash.end())
+ return downloadDataFromGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value()));
+ return QByteArray();
+}
+
+void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId)
+{
+ auto it = m_renderBufferHash.find(bufferId);
+ if (it != m_renderBufferHash.end()) {
+ HGLBuffer glBuffHandle = it.value();
+ GLBuffer *glBuff = m_renderer->nodeManagers()->glBufferManager()->data(glBuffHandle);
+
+ Q_ASSERT(glBuff);
+ // Destroy the GPU resource
+ glBuff->destroy(this);
+ // Destroy the GLBuffer instance
+ m_renderer->nodeManagers()->glBufferManager()->releaseResource(bufferId);
+ // Remove Id - HGLBuffer entry
+ m_renderBufferHash.erase(it);
+ }
+}
+
+bool SubmissionContext::hasGLBufferForBuffer(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId());
+ return (it != m_renderBufferHash.end());
+}
+
+GLBuffer *SubmissionContext::glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type)
+{
+ if (!m_renderBufferHash.contains(buf->peerId()))
+ m_renderBufferHash.insert(buf->peerId(), createGLBufferFor(buf, type));
+ return m_renderer->nodeManagers()->glBufferManager()->data(m_renderBufferHash.value(buf->peerId()));
+}
+
+HGLBuffer SubmissionContext::createGLBufferFor(Buffer *buffer, GLBuffer::Type type)
+{
+ GLBuffer *b = m_renderer->nodeManagers()->glBufferManager()->getOrCreateResource(buffer->peerId());
+ // b.setUsagePattern(static_cast<QOpenGLBuffer::UsagePattern>(buffer->usage()));
+ Q_ASSERT(b);
+ if (!b->create(this))
+ qCWarning(Render::Io) << Q_FUNC_INFO << "buffer creation failed";
+
+ if (!bindGLBuffer(b, type))
+ qCWarning(Render::Io) << Q_FUNC_INFO << "buffer binding failed";
+
+ return m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId());
+}
+
+bool SubmissionContext::bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type)
+{
+ if (type == GLBuffer::ArrayBuffer && buffer == m_boundArrayBuffer)
+ return true;
+
+ if (buffer->bind(this, type)) {
+ if (type == GLBuffer::ArrayBuffer)
+ m_boundArrayBuffer = buffer;
+ return true;
+ }
+ return false;
+}
+
+void SubmissionContext::uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer)
+{
+ if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're uploading, the type doesn't matter here
+ qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed";
+ // 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
+
+ // TO DO: Handle usage pattern
+ 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.constData(), update->data.size(), 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
+ const int bufferSize = buffer->data().size();
+ b->allocate(this, bufferSize, false); // orphan the buffer
+ b->allocate(this, buffer->data().constData(), bufferSize, false);
+ }
+ }
+
+ if (releaseBuffer) {
+ b->release(this);
+ m_boundArrayBuffer = nullptr;
+ }
+ qCDebug(Render::Io) << "uploaded buffer size=" << buffer->data().size();
+}
+
+QByteArray SubmissionContext::downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b)
+{
+ if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here
+ qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed";
+
+ QByteArray data = b->download(this, buffer->data().size());
+ return data;
+}
+
+void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
+ Qt3DCore::QNodeId outputRenderTargetId,
+ QRect inputRect, QRect outputRect,
+ uint defaultFboId,
+ QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
+ QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
+ QBlitFramebuffer::InterpolationMethod interpolationMethod)
+{
+ 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)
+ drawBuffer(GL_COLOR_ATTACHMENT0 + outputAttachmentPoint);
+
+ // 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, mode);
+
+ // Reset draw buffer
+ bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw);
+}
+
+} // namespace Render
+} // namespace Qt3DRender of namespace
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h
new file mode 100644
index 000000000..8efdcbc63
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H
+#define QT3DRENDER_RENDER_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 <Qt3DRender/private/graphicscontext_p.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/private/glbuffer_p.h>
+#include <Qt3DRender/qattribute.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/shadercache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractOpenGLFunctions;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Renderer;
+class GraphicsHelperInterface;
+class RenderStateSet;
+class Material;
+class GLTexture;
+class RenderCommand;
+class RenderTarget;
+class AttachmentPack;
+class Attribute;
+class Buffer;
+class ShaderManager;
+
+enum TextureScope
+{
+ TextureScopeMaterial = 0,
+ TextureScopeTechnique
+ // per-pass for deferred rendering?
+};
+
+typedef QPair<QString, int> NamedUniformLocation;
+
+class Q_AUTOTEST_EXPORT SubmissionContext : public GraphicsContext
+{
+public:
+ SubmissionContext();
+ ~SubmissionContext();
+
+ 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 activateGLHelper();
+ void releaseOpenGL();
+ void setOpenGLContext(QOpenGLContext* ctx);
+
+ // Viewport
+ void setViewport(const QRectF &viewport, const QSize &surfaceSize);
+ QRectF viewport() const { return m_viewport; }
+
+ // Shaders
+ bool activateShader(ProgramDNA shaderDNA);
+ QOpenGLShaderProgram *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);
+
+
+ // Material
+ Material* activeMaterial() const { return m_material; }
+ void setActiveMaterial(Material* rmat);
+
+ // 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 hasGLBufferForBuffer(Buffer *buffer);
+ GLBuffer *glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type);
+
+ // Parameters
+ bool setParameters(ShaderParameterPack &parameterPack);
+
+ // Textures
+ int activateTexture(TextureScope scope, GLTexture* tex, int onUnit = -1);
+ void deactivateTexture(GLTexture *tex);
+
+ // RenderState
+ void setCurrentStateSet(RenderStateSet* ss);
+ RenderStateSet *currentStateSet() const;
+
+ // Wrappers
+ void clearColor(const QColor &color);
+ void clearDepthValue(float depth);
+ void clearStencilValue(int stencil);
+
+private:
+ void initialize();
+
+ // Textures
+ void decayTextureScores();
+ GLint assignUnitForTexture(GLTexture* tex);
+ void deactivateTexturesWithScope(TextureScope ts);
+
+ // 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
+ HGLBuffer createGLBufferFor(Buffer *buffer, GLBuffer::Type type);
+ void uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer = false);
+ QByteArray downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b);
+ bool bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type);
+
+ bool m_initialized;
+ bool m_ownCurrent;
+ const unsigned int m_id;
+ QSurface *m_surface;
+ QSize m_surfaceSize;
+
+ QOpenGLShaderProgram *m_activeShader;
+ ProgramDNA m_activeShaderDNA;
+
+ QHash<Qt3DCore::QNodeId, HGLBuffer> m_renderBufferHash;
+ QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets;
+ QHash<GLuint, QSize> m_renderTargetsSize;
+ QAbstractTexture::TextureFormat m_renderTargetFormat;
+
+ QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers;
+
+ // active textures, indexed by texture unit
+ struct ActiveTexture {
+ GLTexture *texture = nullptr;
+ int score = 0;
+ TextureScope scope = TextureScopeMaterial;
+ bool pinned = false;
+ };
+ QVector<ActiveTexture> m_activeTextures;
+
+ // cache some current state, to make sure we don't issue unnecessary GL calls
+ int m_currClearStencilValue;
+ float m_currClearDepthValue;
+ QColor m_currClearColorValue;
+
+ Material* m_material;
+ QRectF m_viewport;
+ GLuint m_activeFBO;
+
+ GLBuffer *m_boundArrayBuffer;
+ RenderStateSet* m_stateSet;
+ Renderer *m_renderer;
+ QByteArray m_uboTempArray;
+
+
+ // Attributes
+ friend class OpenGLVertexArrayObject;
+ OpenGLVertexArrayObject *m_currentVAO;
+
+ struct VAOVertexAttribute
+ {
+ HGLBuffer bufferHandle;
+ GLBuffer::Type attributeType;
+ int location;
+ GLint dataType;
+ uint byteOffset;
+ uint vertexSize;
+ uint byteStride;
+ uint divisor;
+ GLenum shaderDataType;
+ };
+
+ using VAOIndexAttribute = HGLBuffer;
+ void enableAttribute(const VAOVertexAttribute &attr);
+ void disableAttribute(const VAOVertexAttribute &attr);
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H
diff --git a/src/render/renderers/opengl/io/glbuffer.cpp b/src/render/renderers/opengl/io/glbuffer.cpp
new file mode 100644
index 000000000..f1b860f03
--- /dev/null
+++ b/src/render/renderers/opengl/io/glbuffer.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "glbuffer_p.h"
+#include <private/graphicscontext_p.h>
+
+#if !defined(GL_UNIFORM_BUFFER)
+#define GL_UNIFORM_BUFFER 0x8A11
+#endif
+#if !defined(GL_ARRAY_BUFFER)
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+#if !defined(GL_ELEMENT_ARRAY_BUFFER)
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#endif
+#if !defined(GL_SHADER_STORAGE_BUFFER)
+#define GL_SHADER_STORAGE_BUFFER 0x90D2
+#endif
+#if !defined(GL_PIXEL_PACK_BUFFER)
+#define GL_PIXEL_PACK_BUFFER 0x88EB
+#endif
+#if !defined(GL_PIXEL_UNPACK_BUFFER)
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#endif
+#if !defined(GL_DRAW_INDIRECT_BUFFER)
+#define GL_DRAW_INDIRECT_BUFFER 0x8F3F
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+// A UBO is created for each ShaderData Shader Pair
+// That means a UBO is unique to a shader/shaderdata
+
+namespace {
+
+GLenum glBufferTypes[] = {
+ GL_ARRAY_BUFFER,
+ GL_UNIFORM_BUFFER,
+ GL_ELEMENT_ARRAY_BUFFER,
+ GL_SHADER_STORAGE_BUFFER,
+ GL_PIXEL_PACK_BUFFER,
+ GL_PIXEL_UNPACK_BUFFER,
+ GL_DRAW_INDIRECT_BUFFER
+};
+
+} // anonymous
+
+GLBuffer::GLBuffer()
+ : m_bufferId(0)
+ , m_isCreated(false)
+ , m_bound(false)
+ , m_lastTarget(GL_ARRAY_BUFFER)
+{
+}
+
+bool GLBuffer::bind(GraphicsContext *ctx, Type t)
+{
+ if (m_bufferId == 0)
+ return false;
+ m_lastTarget = glBufferTypes[t];
+ ctx->openGLContext()->functions()->glBindBuffer(m_lastTarget, m_bufferId);
+ m_bound = true;
+ return true;
+}
+
+bool GLBuffer::release(GraphicsContext *ctx)
+{
+ m_bound = false;
+ ctx->openGLContext()->functions()->glBindBuffer(m_lastTarget, 0);
+ return true;
+}
+
+bool GLBuffer::create(GraphicsContext *ctx)
+{
+ ctx->openGLContext()->functions()->glGenBuffers(1, &m_bufferId);
+ m_isCreated = true;
+ return m_bufferId != 0;
+}
+
+void GLBuffer::destroy(GraphicsContext *ctx)
+{
+ ctx->openGLContext()->functions()->glDeleteBuffers(1, &m_bufferId);
+ m_isCreated = false;
+}
+
+void GLBuffer::allocate(GraphicsContext *ctx, uint size, bool dynamic)
+{
+ // Either GL_STATIC_DRAW OR GL_DYNAMIC_DRAW depending on the use case
+ // TO DO: find a way to know how a buffer/QShaderData will be used to use the right usage
+ ctx->openGLContext()->functions()->glBufferData(m_lastTarget, size, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+}
+
+void GLBuffer::allocate(GraphicsContext *ctx, const void *data, uint size, bool dynamic)
+{
+ ctx->openGLContext()->functions()->glBufferData(m_lastTarget, size, data, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+}
+
+void GLBuffer::update(GraphicsContext *ctx, const void *data, uint size, int offset)
+{
+ ctx->openGLContext()->functions()->glBufferSubData(m_lastTarget, offset, size, data);
+}
+
+QByteArray GLBuffer::download(GraphicsContext *ctx, uint size)
+{
+ 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;
+}
+
+void GLBuffer::bindBufferBase(GraphicsContext *ctx, int bindingPoint, GLBuffer::Type t)
+{
+ ctx->bindBufferBase(glBufferTypes[t], bindingPoint, m_bufferId);
+}
+
+void GLBuffer::bindBufferBase(GraphicsContext *ctx, int bindingPoint)
+{
+ ctx->bindBufferBase(m_lastTarget, bindingPoint, m_bufferId);
+}
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/io/glbuffer_p.h b/src/render/renderers/opengl/io/glbuffer_p.h
new file mode 100644
index 000000000..731634b6b
--- /dev/null
+++ b/src/render/renderers/opengl/io/glbuffer_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GLBUFFER_P_H
+#define QT3DRENDER_RENDER_GLBUFFER_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 <QOpenGLContext>
+#include <Qt3DCore/qnodeid.h>
+#include <qbytearray.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class GraphicsContext;
+
+class GLBuffer
+{
+public:
+ GLBuffer();
+
+ enum Type
+ {
+ ArrayBuffer = 0,
+ UniformBuffer,
+ IndexBuffer,
+ ShaderStorageBuffer,
+ PixelPackBuffer,
+ PixelUnpackBuffer,
+ DrawIndirectBuffer
+ };
+
+ bool bind(GraphicsContext *ctx, Type t);
+ bool release(GraphicsContext *ctx);
+ bool create(GraphicsContext *ctx);
+ void destroy(GraphicsContext *ctx);
+ void allocate(GraphicsContext *ctx, uint size, bool dynamic = true);
+ void allocate(GraphicsContext *ctx, const void *data, uint size, bool dynamic = true);
+ void update(GraphicsContext *ctx, const void *data, uint size, int offset = 0);
+ QByteArray download(GraphicsContext *ctx, uint size);
+ void bindBufferBase(GraphicsContext *ctx, int bindingPoint, Type t);
+ void bindBufferBase(GraphicsContext *ctx, int bindingPoint);
+
+ inline GLuint bufferId() const { return m_bufferId; }
+ inline bool isCreated() const { return m_isCreated; }
+ inline bool isBound() const { return m_bound; }
+
+private:
+ GLuint m_bufferId;
+ bool m_isCreated;
+ bool m_bound;
+ GLenum m_lastTarget;
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GLBUFFER_P_H
diff --git a/src/render/renderers/opengl/io/io.pri b/src/render/renderers/opengl/io/io.pri
new file mode 100644
index 000000000..462978c4d
--- /dev/null
+++ b/src/render/renderers/opengl/io/io.pri
@@ -0,0 +1,8 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/glbuffer.cpp
+
+HEADERS += \
+ $$PWD/glbuffer_p.h
+
diff --git a/src/render/renderers/opengl/jobs/filtercompatibletechniquejob.cpp b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob.cpp
new file mode 100644
index 000000000..342fd3dad
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "filtercompatibletechniquejob_p.h"
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+#include <Qt3DRender/private/submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+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 Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/jobs/filtercompatibletechniquejob_p.h b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob_p.h
new file mode 100644
index 000000000..4f7a7146c
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_FILTERCOMPATIBLETECHNIQUEJOB_H
+#define QT3DRENDER_RENDER_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;
+class Renderer;
+
+class QT3DRENDERSHARED_PRIVATE_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 Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_FILTERCOMPATIBLETECHNIQUEJOB_H
diff --git a/src/render/renderers/opengl/jobs/jobs.pri b/src/render/renderers/opengl/jobs/jobs.pri
new file mode 100644
index 000000000..021cd3242
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/jobs.pri
@@ -0,0 +1,15 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/filtercompatibletechniquejob.cpp \
+ $$PWD/materialparametergathererjob.cpp \
+ $$PWD/renderviewbuilderjob.cpp \
+ $$PWD/renderviewinitializerjob.cpp \
+ $$PWD/renderviewjobutils.cpp
+
+HEADERS += \
+ $$PWD/filtercompatibletechniquejob_p.h \
+ $$PWD/materialparametergathererjob_p.h \
+ $$PWD/renderviewbuilderjob_p.h \
+ $$PWD/renderviewinitializerjob_p.h \
+ $$PWD/renderviewjobutils_p.h
diff --git a/src/render/renderers/opengl/jobs/materialparametergathererjob.cpp b/src/render/renderers/opengl/jobs/materialparametergathererjob.cpp
new file mode 100644
index 000000000..bae516c7e
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/materialparametergathererjob.cpp
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** 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 {
+
+int materialParameterGathererCounter = 0;
+const int likelyNumberOfParameters = 24;
+
+} // anonymous
+
+MaterialParameterGathererJob::MaterialParameterGathererJob()
+ : Qt3DCore::QAspectJob()
+ , m_manager(nullptr)
+ , m_techniqueFilter(nullptr)
+ , m_renderPassFilter(nullptr)
+ , m_renderer(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_renderer, 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});
+ }
+ }
+ }
+ }
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/jobs/materialparametergathererjob_p.h b/src/render/renderers/opengl/jobs/materialparametergathererjob_p.h
new file mode 100644
index 000000000..687163387
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/materialparametergathererjob_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** 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_MATERIALPARAMETERGATHERERJOB_P_H
+#define QT3DRENDER_RENDER_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/renderviewjobutils_p.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class NodeManagers;
+class TechniqueFilter;
+class RenderPassFilter;
+class Renderer;
+
+// TO be executed for each FrameGraph branch with a given RenderPassFilter/TechniqueFilter
+
+class QT3DRENDERSHARED_PRIVATE_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 void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; }
+ inline const MaterialParameterGathererData &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;
+ Renderer *m_renderer;
+
+ // Material id to array of RenderPasse with parameters
+ MaterialParameterGathererData m_parameters;
+ QVector<HMaterial> m_handles;
+};
+
+typedef QSharedPointer<MaterialParameterGathererJob> MaterialParameterGathererJobPtr;
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_MATERIALPARAMETERGATHERERJOB_P_H
diff --git a/src/render/renderers/opengl/jobs/renderviewbuilderjob.cpp b/src/render/renderers/opengl/jobs/renderviewbuilderjob.cpp
new file mode 100644
index 000000000..fa6218d6f
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/renderviewbuilderjob.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** 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 "renderviewbuilderjob_p.h"
+#include <Qt3DRender/private/job_common_p.h>
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/renderview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace {
+int renderViewInstanceCounter = 0;
+} // anonymous
+
+RenderViewBuilderJob::RenderViewBuilderJob()
+ : Qt3DCore::QAspectJob(),
+ m_renderView(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderViewBuilder, renderViewInstanceCounter++);
+}
+
+void RenderViewBuilderJob::run()
+{
+ // Build RenderCommand should perform the culling as we have no way to determine
+ // if a child has a mesh in the view frustum while its parent isn't contained in it.
+ if (!m_renderView->noDraw()) {
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ gatherLightsTime = timer.nsecsElapsed();
+ timer.restart();
+#endif
+ if (!m_renderView->isCompute())
+ m_commands = m_renderView->buildDrawRenderCommands(m_renderables);
+ else
+ m_commands = m_renderView->buildComputeRenderCommands(m_renderables);
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ buildCommandsTime = timer.nsecsElapsed();
+ timer.restart();
+#endif
+ }
+
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ qint64 creationTime = timer.nsecsElapsed();
+ timer.restart();
+#endif
+
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ qint64 sortTime = timer.nsecsElapsed();
+#endif
+
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ qDebug() << m_index
+ << "state:" << gatherStateTime / 1.0e6
+ << "lights:" << gatherLightsTime / 1.0e6
+ << "build commands:" << buildCommandsTime / 1.0e6
+ << "sort:" << sortTime / 1.0e6;
+#endif
+
+
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/jobs/renderviewbuilderjob_p.h b/src/render/renderers/opengl/jobs/renderviewbuilderjob_p.h
new file mode 100644
index 000000000..c38f788b0
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/renderviewbuilderjob_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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_RENDERVIEWBUILDERJOB_P_H
+#define QT3DRENDER_RENDER_RENDERVIEWBUILDERJOB_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>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class RenderView;
+class Renderer;
+class RenderCommand;
+
+class Q_AUTOTEST_EXPORT RenderViewBuilderJob : public Qt3DCore::QAspectJob
+{
+public:
+ RenderViewBuilderJob();
+
+ inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; }
+ inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; }
+ inline void setIndex(int index) Q_DECL_NOTHROW { m_index = index; }
+ inline void setRenderables(const QVector<Entity *> &renderables) Q_DECL_NOTHROW { m_renderables = renderables; }
+ QVector<RenderCommand *> &commands() Q_DECL_NOTHROW { return m_commands; }
+
+ void run() final;
+
+private:
+ RenderView *m_renderView;
+ Renderer *m_renderer;
+ int m_index;
+ QVector<Entity *> m_renderables;
+ QVector<RenderCommand *> m_commands;
+};
+
+typedef QSharedPointer<RenderViewBuilderJob> RenderViewBuilderJobPtr;
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERVIEWBUILDERJOB_P_H
diff --git a/src/render/renderers/opengl/jobs/renderviewinitializerjob.cpp b/src/render/renderers/opengl/jobs/renderviewinitializerjob.cpp
new file mode 100644
index 000000000..7bf55be40
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/renderviewinitializerjob.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 <Qt3DRender/private/renderview_p.h>
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/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 {
+// 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 Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/jobs/renderviewinitializerjob_p.h b/src/render/renderers/opengl/jobs/renderviewinitializerjob_p.h
new file mode 100644
index 000000000..fb4e2c67c
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/renderviewinitializerjob_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERVIEWINITIALIZERJOB_H
+#define QT3DRENDER_RENDER_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 Renderer;
+class FrameGraphNode;
+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 Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERVIEWINITIALIZERJOB_H
diff --git a/src/render/renderers/opengl/jobs/renderviewjobutils.cpp b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
new file mode 100644
index 000000000..ffad387c6
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
@@ -0,0 +1,540 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderviewjobutils_p.h"
+#include "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/renderstateset_p.h>
+#include <Qt3DRender/private/rendertargetselectornode_p.h>
+#include <Qt3DRender/private/renderview_p.h>
+#include <Qt3DRender/private/sortpolicy_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/shadervariables_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>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+
+/*!
+ \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(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::SubtreeSelector:
+ // Has no meaning here. SubtreeSelector was used
+ // in a prior step to build 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);
+ }
+
+ if (rStateSet->hasRenderStates())
+ addToRenderStateSet(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());
+ }
+ 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: {
+ const Render::MemoryBarrier *barrier = static_cast<const Render::MemoryBarrier *>(node);
+ rv->setMemoryBarrier(barrier->waitOperations()|rv->memoryBarrier());
+ 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;
+ }
+
+ default:
+ // Should never get here
+ qCWarning(Backend) << "Unhandled FrameGraphNode type";
+ }
+
+ node = node->parent();
+ }
+}
+
+/*!
+ \internal
+ Searches the \a renderer for the best matching Technique from
+ \a effect specified by the \a renderView.
+*/
+Technique *findTechniqueForEffect(Renderer *renderer,
+ const TechniqueFilter *techniqueFilter,
+ Effect *effect)
+{
+ if (!effect)
+ return nullptr;
+
+ NodeManagers *manager = renderer->nodeManagers();
+ 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) Technique
+ // 3) Effect
+ //
+ // 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, technique);
+ parametersFromParametersProvider(infoList, manager, effect);
+}
+
+void addToRenderStateSet(RenderStateSet *stateSet,
+ const QVector<Qt3DCore::QNodeId> stateIds,
+ RenderStateManager *manager)
+{
+ for (const Qt3DCore::QNodeId &stateId : stateIds) {
+ RenderStateNode *node = manager->lookupResource(stateId);
+ if (node->isEnabled())
+ 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(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 = blockName + QLatin1String(".") + qmlPropertyName + 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 = blockName + QLatin1Char('.') + 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
+ if (currentShaderData->propertyTransformType(qmlPropertyName) != ShaderData::NoTransform)
+ activeUniformNamesToValue.insert(StringToInt::lookupId(varName),
+ currentShaderData->getTransformedProperty(qmlPropertyName, viewMatrix));
+ else
+ activeUniformNamesToValue.insert(StringToInt::lookupId(varName), value);
+ }
+ }
+}
+
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName)
+{
+ const QHash<QString, QVariant> &properties = rShaderData->properties();
+ QHash<QString, QVariant>::const_iterator it = properties.begin();
+ const QHash<QString, QVariant>::const_iterator end = properties.end();
+
+ while (it != end) {
+ const auto prefix = qmlPropertyName.isEmpty() ? QLatin1String("") : QLatin1String(".");
+ buildActiveUniformNameValueMapHelper(rShaderData,
+ blockName + prefix + qmlPropertyName,
+ it.key(),
+ it.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 Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/jobs/renderviewjobutils_p.h b/src/render/renderers/opengl/jobs/renderviewjobutils_p.h
new file mode 100644
index 000000000..468a95bfd
--- /dev/null
+++ b/src/render/renderers/opengl/jobs/renderviewjobutils_p.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDERVIEWJOBUTILS_P_H
+#define QT3DRENDER_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>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+class QFrameAllocator;
+}
+
+namespace Qt3DRender {
+namespace Render {
+
+class FrameGraphNode;
+class ParameterManager;
+class Effect;
+class Entity;
+class Material;
+class RenderPass;
+class RenderStateSet;
+class Technique;
+class RenderView;
+class TechniqueFilter;
+class RenderPassFilter;
+class Renderer;
+class NodeManagers;
+class ShaderDataManager;
+struct ShaderUniform;
+class ShaderData;
+class TextureManager;
+class RenderStateManager;
+class RenderStateCollection;
+
+Q_AUTOTEST_EXPORT void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv,
+ const FrameGraphNode *fgLeaf);
+
+Q_AUTOTEST_EXPORT Technique *findTechniqueForEffect(Renderer *renderer,
+ 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>
+Q_AUTOTEST_EXPORT 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_2(Qt3DRender, Render, 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 addToRenderStateSet(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(ShaderData *currentShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName,
+ const QVariant &value);
+ void buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName = QString());
+
+ bool updatedPropertiesOnly;
+ QHash<QString, ShaderUniform> uniforms;
+ UniformBlockValueBuilderHash activeUniformNamesToValue;
+ ShaderDataManager *shaderDataManager;
+ TextureManager *textureManager;
+ Matrix4x4 viewMatrix;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDERVIEWJOBUTILS_P_H
diff --git a/src/render/renderers/opengl/opengl.pri b/src/render/renderers/opengl/opengl.pri
new file mode 100644
index 000000000..a669bf4ba
--- /dev/null
+++ b/src/render/renderers/opengl/opengl.pri
@@ -0,0 +1,18 @@
+
+include (renderer/renderer.pri)
+include (jobs/jobs.pri)
+include (io/io.pri)
+include (textures/textures.pri)
+include (graphicshelpers/graphicshelpers.pri)
+include (renderstates/renderstates.pri)
+
+# 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
+}
+
+# otherwise mingw headers do not declare common functions like ::strcasecmp
+win32-g++*:QMAKE_CXXFLAGS_CXX11 = -std=gnu++0x
diff --git a/src/render/renderers/opengl/renderer/commandthread.cpp b/src/render/renderers/opengl/renderer/commandthread.cpp
new file mode 100644
index 000000000..387fc1113
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/commandthread.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "commandthread_p.h"
+#include <Qt3DRender/private/glcommands_p.h>
+#include <Qt3DRender/private/offscreensurfacehelper_p.h>
+#include <Qt3DRender/private/graphicscontext_p.h>
+#include <Qt3DRender/private/shadercache_p.h>
+#include <QOpenGLContext>
+#include <QOffscreenSurface>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+CommandThread::CommandThread(Renderer *renderer)
+ : QThread()
+ , m_renderer(renderer)
+ , m_waitForStartSemaphore(0)
+ , m_initializedSemaphore(0)
+ , m_commandRequestedSemaphore(0)
+ , m_commandExecutionSemaphore(0)
+ , m_mainContext(nullptr)
+ , m_shaderCache(nullptr)
+ , m_offsreenSurfaceHelper(nullptr)
+ , m_currentCommand(nullptr)
+ , m_running(0)
+{
+}
+
+CommandThread::~CommandThread()
+{
+}
+
+void CommandThread::setShaderCache(ShaderCache *shaderCache)
+{
+ m_shaderCache = shaderCache;
+}
+
+// Called by RenderThread or MainThread (Scene3d)
+void CommandThread::initialize(QOpenGLContext *mainContext, OffscreenSurfaceHelper *offsreenSurfaceHelper)
+{
+ // Start the thread
+ start();
+
+ // Wait for thread to be started
+ m_waitForStartSemaphore.acquire();
+
+ m_mainContext = mainContext;
+ m_offsreenSurfaceHelper = offsreenSurfaceHelper;
+ Q_ASSERT(m_mainContext && offsreenSurfaceHelper);
+ m_running.fetchAndStoreOrdered(1);
+
+ // Allow thread to proceed
+ m_initializedSemaphore.release();
+}
+
+// Called by RenderThread or MainThread (Scene3D)
+void CommandThread::shutdown()
+{
+ m_running.fetchAndStoreOrdered(0);
+
+ // Unblock thread
+ m_commandRequestedSemaphore.release(1);
+
+ // Wait for thread to exit
+ wait();
+
+ // Reset semaphores (in case we ever want to restart)
+ m_waitForStartSemaphore.acquire(m_waitForStartSemaphore.available());
+ m_initializedSemaphore.acquire(m_initializedSemaphore.available());
+ m_commandRequestedSemaphore.acquire(m_commandRequestedSemaphore.available());
+ m_commandExecutionSemaphore.acquire(m_commandExecutionSemaphore.available());
+ m_localContext.reset();
+}
+
+// Any thread can call this, this is a blocking command
+void CommandThread::executeCommand(GLCommand *command)
+{
+ if (!isRunning())
+ return;
+
+ // We lock to prevent any other call to executeCommand to be executed
+ // before we have received the result of our command
+ m_blockingCallerMutex.lock();
+
+ // Store command to be executed
+ m_currentCommand = command;
+
+ // Allow thread to proceed and execute command
+ m_commandRequestedSemaphore.release();
+
+ // Wait for thread to be done
+ m_commandExecutionSemaphore.acquire();
+
+ // Reset command
+ m_currentCommand = nullptr;
+
+ // Unlock blocking semaphore so that other calls to executeCommand
+ // can proceed
+ m_blockingCallerMutex.unlock();
+}
+
+void CommandThread::run()
+{
+ // Allow initialize to proceed
+ m_waitForStartSemaphore.release();
+
+ // Wait for initialize to be completed
+ m_initializedSemaphore.acquire();
+
+ Q_ASSERT(m_mainContext && m_shaderCache);
+
+ // Initialize shared context and resources for the thread
+ m_localContext.reset(new QOpenGLContext());
+ m_localContext->setShareContext(m_mainContext);
+ m_localContext->create();
+
+ // Initialize GraphicsContext
+ m_graphicsContext.reset(new GraphicsContext());
+ m_graphicsContext->setShaderCache(m_shaderCache);
+ m_graphicsContext->setOpenGLContext(m_localContext.data());
+
+ bool initialized = false;
+ while (true) {
+
+ // Wait for command
+ m_commandRequestedSemaphore.acquire();
+
+ // Are we still running?
+ if (!m_running.load()) {
+ m_graphicsContext->doneCurrent();
+ // to prevent executeCommand being locked
+ m_commandExecutionSemaphore.release();
+ break;
+ }
+
+ if (Q_UNLIKELY(!initialized)) {
+ QOffscreenSurface *offscreenSurface = m_offsreenSurfaceHelper->offscreenSurface();
+ Q_ASSERT(offscreenSurface);
+ m_graphicsContext->makeCurrent(offscreenSurface);
+ initialized = true;
+ }
+
+ m_currentCommand->execute(m_renderer, m_graphicsContext.data());
+
+ // Allow caller to proceed as we are done with the command
+ m_commandExecutionSemaphore.release();
+ }
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/commandthread_p.h b/src/render/renderers/opengl/renderer/commandthread_p.h
new file mode 100644
index 000000000..0508675c4
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/commandthread_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_COMMANDTHREAD_P_H
+#define QT3DRENDER_RENDER_COMMANDTHREAD_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QThread>
+#include <QtCore/QSemaphore>
+#include <QtCore/QMutex>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLContext;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Renderer;
+class GLCommand;
+class OffscreenSurfaceHelper;
+class GraphicsContext;
+class ShaderCache;
+
+class CommandThread : public QThread
+{
+ Q_OBJECT
+public:
+ explicit CommandThread(Renderer *renderer);
+ ~CommandThread();
+
+ Render::Renderer* renderer() const { return m_renderer; }
+
+ void setShaderCache(ShaderCache *shaderCache);
+ ShaderCache *shaderCache() const { return m_shaderCache; }
+
+ void initialize(QOpenGLContext *mainContext, OffscreenSurfaceHelper *offsreenSurfaceHelper);
+ void shutdown();
+
+ void executeCommand(GLCommand *command);
+
+private:
+ void run() override;
+ void executeCommandInternal(Qt3DRender::Render::GLCommand *command);
+
+private:
+ Renderer* m_renderer;
+ QSemaphore m_waitForStartSemaphore;
+ QSemaphore m_initializedSemaphore;
+ QSemaphore m_commandRequestedSemaphore;
+ QSemaphore m_commandExecutionSemaphore;
+ QMutex m_blockingCallerMutex;
+ QOpenGLContext *m_mainContext;
+ ShaderCache *m_shaderCache;
+ OffscreenSurfaceHelper *m_offsreenSurfaceHelper;
+ QScopedPointer<QOpenGLContext> m_localContext;
+ QScopedPointer<GraphicsContext> m_graphicsContext;
+ GLCommand *m_currentCommand;
+ QAtomicInt m_running;
+};
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_COMMANDTHREAD_P_H
diff --git a/src/render/renderers/opengl/renderer/glcommands.cpp b/src/render/renderers/opengl/renderer/glcommands.cpp
new file mode 100644
index 000000000..fd7ee9fe8
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/glcommands.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "glcommands_p.h"
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/graphicscontext_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+LoadShaderCommand::LoadShaderCommand(Shader *shader)
+ : m_shader(shader)
+{
+ Q_ASSERT(m_shader);
+}
+
+void LoadShaderCommand::execute(Renderer *renderer, GraphicsContext *ctx)
+{
+ NodeManagers *nodeManagers = renderer->nodeManagers();
+ ctx->loadShader(m_shader, nodeManagers->shaderManager());
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/glcommands_p.h b/src/render/renderers/opengl/renderer/glcommands_p.h
new file mode 100644
index 000000000..5ed360759
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/glcommands_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GLCOMMANDS_P_H
+#define QT3DRENDER_RENDER_GLCOMMANDS_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>
+
+QT_BEGIN_NAMESPACE
+
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class GraphicsContext;
+class Renderer;
+class Shader;
+
+class GLCommand
+{
+public:
+ virtual void execute(Renderer *renderer, GraphicsContext *ctx) = 0;
+};
+
+class Q_AUTOTEST_EXPORT LoadShaderCommand : public GLCommand
+{
+public:
+ explicit LoadShaderCommand(Shader *shader);
+ Shader *shader() const { return m_shader; }
+ void execute(Renderer *renderer, GraphicsContext *ctx) Q_DECL_OVERRIDE;
+
+private:
+ Shader *m_shader = nullptr;
+};
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GLCOMMANDS_P_H
diff --git a/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp b/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp
new file mode 100644
index 000000000..0c4fd8c9d
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "openglvertexarrayobject_p.h"
+#include <Qt3DRender/private/submissioncontext_p.h>
+#include <Qt3DRender/private/renderer_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+OpenGLVertexArrayObject::OpenGLVertexArrayObject()
+ : m_ctx(nullptr)
+ , m_specified(false)
+ , m_supportsVao(false)
+{}
+
+void OpenGLVertexArrayObject::bind()
+{
+ Q_ASSERT(m_ctx);
+ if (m_supportsVao) {
+ Q_ASSERT(!m_vao.isNull());
+ Q_ASSERT(m_vao->isCreated());
+ m_vao->bind();
+ } else {
+ // Unbind any other VAO that may have been bound and not released correctly
+ if (m_ctx->m_currentVAO != nullptr && m_ctx->m_currentVAO != this)
+ m_ctx->m_currentVAO->release();
+
+ m_ctx->m_currentVAO = this;
+ // We need to specify array and vertex attributes
+ for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes))
+ m_ctx->enableAttribute(attr);
+ if (!m_indexAttribute.isNull())
+ m_ctx->bindGLBuffer(m_ctx->m_renderer->nodeManagers()->glBufferManager()->data(m_indexAttribute),
+ GLBuffer::IndexBuffer);
+ }
+}
+
+void OpenGLVertexArrayObject::release()
+{
+ Q_ASSERT(m_ctx);
+ if (m_supportsVao) {
+ Q_ASSERT(!m_vao.isNull());
+ Q_ASSERT(m_vao->isCreated());
+ m_vao->release();
+ } else {
+ if (m_ctx->m_currentVAO == this) {
+ for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes))
+ m_ctx->disableAttribute(attr);
+ m_ctx->m_currentVAO = nullptr;
+ }
+ }
+}
+
+// called from Render thread
+void OpenGLVertexArrayObject::create(SubmissionContext *ctx, const VAOIdentifier &key)
+{
+ QMutexLocker lock(&m_mutex);
+
+ Q_ASSERT(!m_ctx && !m_vao);
+
+ m_ctx = ctx;
+ m_supportsVao = m_ctx->supportsVAO();
+ if (m_supportsVao) {
+ m_vao.reset(new QOpenGLVertexArrayObject());
+ m_vao->create();
+ }
+ m_owners = key;
+}
+
+// called from Render thread
+void OpenGLVertexArrayObject::destroy()
+{
+ QMutexLocker locker(&m_mutex);
+
+ Q_ASSERT(m_ctx);
+ cleanup();
+}
+
+void OpenGLVertexArrayObject::cleanup()
+{
+ m_vao.reset();
+ m_ctx = nullptr;
+ m_specified = false;
+ m_supportsVao = false;
+ m_indexAttribute = SubmissionContext::VAOIndexAttribute();
+ m_vertexAttributes.clear();
+}
+
+// called from job
+bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr)
+{
+ QMutexLocker lock(&m_mutex);
+
+ if (!m_ctx)
+ return false;
+
+ const bool geometryExists = (geomMgr->data(m_owners.first) != nullptr);
+ const bool shaderExists = (shaderMgr->data(m_owners.second) != nullptr);
+
+ return !geometryExists || !shaderExists;
+}
+
+void OpenGLVertexArrayObject::saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr)
+{
+ // Remove any vertexAttribute already at location
+ for (auto i = m_vertexAttributes.size() - 1; i >= 0; --i) {
+ if (m_vertexAttributes.at(i).location == attr.location) {
+ m_vertexAttributes.removeAt(i);
+ break;
+ }
+ }
+ m_vertexAttributes.push_back(attr);
+}
+
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h b/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h
new file mode 100644
index 000000000..eee837221
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OPENGLVERTEXARRAYOBJECT_H
+#define OPENGLVERTEXARRAYOBJECT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qopenglvertexarrayobject.h>
+#include <Qt3DRender/private/submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class GeometryManager;
+class ShaderManager;
+
+typedef QPair<HGeometry, HShader> VAOIdentifier;
+
+class OpenGLVertexArrayObject
+{
+public:
+ OpenGLVertexArrayObject();
+
+ void bind();
+ void release();
+
+ void create(SubmissionContext *ctx, const VAOIdentifier &key);
+ void destroy();
+ void cleanup();
+
+ bool isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr);
+
+ QOpenGLVertexArrayObject *vao() { return m_vao.data(); }
+ const QOpenGLVertexArrayObject *vao() const { return m_vao.data(); }
+
+ void setSpecified(bool b) { m_specified = b; }
+ bool isSpecified() const { return m_specified; }
+
+
+private:
+ QMutex m_mutex;
+ SubmissionContext *m_ctx;
+ QScopedPointer<QOpenGLVertexArrayObject> m_vao;
+ bool m_specified;
+ bool m_supportsVao;
+ VAOIdentifier m_owners;
+
+ friend class SubmissionContext;
+
+ void saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr);
+ inline void saveIndexAttribute(HGLBuffer glBufferHandle) { m_indexAttribute = glBufferHandle; }
+
+ QVector<SubmissionContext::VAOVertexAttribute> m_vertexAttributes;
+ SubmissionContext::VAOIndexAttribute m_indexAttribute;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // OPENGLVERTEXARRAYOBJECT_H
diff --git a/src/render/renderers/opengl/renderer/rendercommand.cpp b/src/render/renderers/opengl/renderer/rendercommand.cpp
new file mode 100644
index 000000000..e60b17668
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/rendercommand.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rendercommand_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+RenderCommand::RenderCommand()
+ : m_stateSet(nullptr)
+ , m_depth(0.0f)
+ , m_changeCost(0)
+ , m_type(RenderCommand::Draw)
+ , m_primitiveCount(0)
+ , m_primitiveType(QGeometryRenderer::Triangles)
+ , m_restartIndexValue(-1)
+ , m_firstInstance(0)
+ , m_firstVertex(0)
+ , m_verticesPerPatch(0)
+ , m_instanceCount(0)
+ , m_indexOffset(0)
+ , m_indexAttributeByteOffset(0)
+ , m_indexAttributeDataType(GL_UNSIGNED_SHORT)
+ , m_indirectAttributeByteOffset(0)
+ , m_drawIndexed(false)
+ , m_drawIndirect(false)
+ , m_primitiveRestartEnabled(false)
+ , m_isValid(false)
+{
+ m_workGroups[0] = 0;
+ m_workGroups[1] = 0;
+ m_workGroups[2] = 0;
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/rendercommand_p.h b/src/render/renderers/opengl/renderer/rendercommand_p.h
new file mode 100644
index 000000000..67e02d35b
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/rendercommand_p.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERCOMMAND_H
+#define QT3DRENDER_RENDER_RENDERCOMMAND_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <Qt3DRender/private/shaderparameterpack_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QMatrix4x4>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLVertexArrayObject;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class RenderStateSet;
+
+class Q_AUTOTEST_EXPORT RenderCommand
+{
+public:
+ RenderCommand();
+
+ HVao m_vao; // VAO used during the submission step to store all states and VBOs
+ HShader m_shader; // Shader for given pass and mesh
+ HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....)
+ ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy the
+ // Texture while submission is happening.
+ RenderStateSet *m_stateSet;
+
+ HGeometry m_geometry;
+ HGeometryRenderer m_geometryRenderer;
+
+ HBuffer m_indirectDrawBuffer; // Reference to indirect draw buffer (valid only m_drawIndirect == true)
+
+ // A QAttribute pack might be interesting
+ // This is a temporary fix in the meantime, to remove the hacked methods in Technique
+ QVector<int> m_attributes;
+
+ float m_depth;
+ int m_changeCost;
+ uint m_shaderDna;
+
+ enum CommandType {
+ Draw,
+ Compute
+ };
+
+ CommandType m_type;
+ int m_workGroups[3];
+
+ // Values filled for draw calls
+ GLsizei m_primitiveCount;
+ QGeometryRenderer::PrimitiveType m_primitiveType;
+ int m_restartIndexValue;
+ int m_firstInstance;
+ int m_firstVertex;
+ int m_verticesPerPatch;
+ int m_instanceCount;
+ int m_indexOffset;
+ uint m_indexAttributeByteOffset;
+ GLint m_indexAttributeDataType;
+ uint m_indirectAttributeByteOffset;
+ bool m_drawIndexed;
+ bool m_drawIndirect;
+ bool m_primitiveRestartEnabled;
+ bool m_isValid;
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERCOMMAND_H
diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp
new file mode 100644
index 000000000..2611fb6cc
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderer.cpp
@@ -0,0 +1,2067 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderer_p.h"
+
+#include <Qt3DCore/qentity.h>
+
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qmesh.h>
+#include <Qt3DRender/qrenderpass.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qeffect.h>
+
+#include <Qt3DRender/private/qsceneimporter_p.h>
+#include <Qt3DRender/private/renderstates_p.h>
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/framegraphvisitor_p.h>
+#include <Qt3DRender/private/graphicscontext_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/rendercommand_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/renderqueue_p.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+#include <Qt3DRender/private/glbuffer_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <Qt3DRender/private/technique_p.h>
+#include <Qt3DRender/private/renderthread_p.h>
+#include <Qt3DRender/private/renderview_p.h>
+#include <Qt3DRender/private/scenemanager_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/vsyncframeadvanceservice_p.h>
+#include <Qt3DRender/private/pickeventfilter_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/gltexturemanager_p.h>
+#include <Qt3DRender/private/gltexture_p.h>
+#include <Qt3DRender/private/geometryrenderermanager_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/openglvertexarrayobject_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/renderviewbuilder_p.h>
+#include <Qt3DRender/private/commandthread_p.h>
+#include <Qt3DRender/private/glcommands_p.h>
+
+#include <Qt3DRender/qcameralens.h>
+#include <Qt3DCore/private/qeventfilterservice_p.h>
+#include <Qt3DCore/private/qabstractaspectjobmanager_p.h>
+#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+
+#if QT_CONFIG(qt3d_profile_jobs)
+#include <Qt3DCore/private/aspectcommanddebugger_p.h>
+#endif
+
+#include <QStack>
+#include <QOffscreenSurface>
+#include <QSurface>
+#include <QElapsedTimer>
+#include <QLibraryInfo>
+#include <QPluginLoader>
+#include <QDir>
+#include <QUrl>
+#include <QOffscreenSurface>
+#include <QWindow>
+
+#include <QtGui/private/qopenglcontext_p.h>
+
+// For Debug purposes only
+#include <QThread>
+
+
+#if QT_CONFIG(qt3d_profile_jobs)
+#include <Qt3DCore/private/qthreadpooler_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+#include <Qt3DRender/private/commandexecuter_p.h>
+#endif
+
+#include <Qt3DRender/private/frameprofiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+/*!
+ \internal
+
+ Renderer shutdown procedure:
+
+ Since the renderer relies on the surface and OpenGLContext to perform its cleanup,
+ it is shutdown when the surface is set to nullptr
+
+ When the surface is set to nullptr this will request the RenderThread to terminate
+ and will prevent createRenderBinJobs from returning a set of jobs as there is nothing
+ more to be rendered.
+
+ In turn, this will call shutdown which will make the OpenGL context current one last time
+ to allow cleanups requiring a call to QOpenGLContext::currentContext to execute properly.
+ At the end of that function, the GraphicsContext is set to null.
+
+ At this point though, the QAspectThread is still running its event loop and will only stop
+ a short while after.
+ */
+
+Renderer::Renderer(QRenderAspect::RenderType type)
+ : m_services(nullptr)
+ , m_nodesManager(nullptr)
+ , m_renderSceneRoot(nullptr)
+ , m_defaultRenderStateSet(nullptr)
+ , m_submissionContext(nullptr)
+ , m_renderQueue(new RenderQueue())
+ , m_renderThread(type == QRenderAspect::Threaded ? new RenderThread(this) : nullptr)
+ , m_commandThread(new CommandThread(this))
+ , m_vsyncFrameAdvanceService(new VSyncFrameAdvanceService(m_renderThread != nullptr))
+ , m_waitForInitializationToBeCompleted(0)
+ , m_pickEventFilter(new PickEventFilter())
+ , m_exposed(0)
+ , m_lastFrameCorrect(0)
+ , m_glContext(nullptr)
+ , m_shareContext(nullptr)
+ , m_shaderCache(new ShaderCache())
+ , m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create())
+ , m_rayCastingJob(RayCastingJobPtr::create())
+ , m_time(0)
+ , m_settings(nullptr)
+ , m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create())
+ , m_cleanupJob(Render::FrameCleanupJobPtr::create())
+ , m_worldTransformJob(Render::UpdateWorldTransformJobPtr::create())
+ , m_expandBoundingVolumeJob(Render::ExpandBoundingVolumeJobPtr::create())
+ , m_calculateBoundingVolumeJob(Render::CalculateBoundingVolumeJobPtr::create())
+ , m_updateWorldBoundingVolumeJob(Render::UpdateWorldBoundingVolumeJobPtr::create())
+ , m_updateTreeEnabledJob(Render::UpdateTreeEnabledJobPtr::create())
+ , m_sendRenderCaptureJob(Render::SendRenderCaptureJobPtr::create(this))
+ , m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create())
+ , m_updateSkinningPaletteJob(Render::UpdateSkinningPaletteJobPtr::create())
+ , m_updateLevelOfDetailJob(Render::UpdateLevelOfDetailJobPtr::create())
+ , m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create())
+ , m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create())
+ , m_bufferGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering))
+ , m_vaoGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering))
+ , m_textureGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering))
+ , m_introspectShaderJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { reloadDirtyShaders(); }, JobTypes::DirtyShaderGathering))
+ , m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading))
+ , m_ownedContext(false)
+ , m_offscreenHelper(nullptr)
+ #if QT_CONFIG(qt3d_profile_jobs)
+ , m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this))
+ #endif
+{
+ // Set renderer as running - it will wait in the context of the
+ // RenderThread for RenderViews to be submitted
+ m_running.fetchAndStoreOrdered(1);
+ if (m_renderThread)
+ m_renderThread->waitForStart();
+
+ // Create jobs to update transforms and bounding volumes
+ // We can only update bounding volumes once all world transforms are known
+ m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob);
+ m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob);
+ m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob);
+ m_updateShaderDataTransformJob->addDependency(m_worldTransformJob);
+ m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob);
+ // m_calculateBoundingVolumeJob's dependency on m_updateTreeEnabledJob is set in renderBinJobs
+
+ // Dirty texture gathering depends on m_syncTextureLoadingJob
+ // m_syncTextureLoadingJob will depend on the texture loading jobs
+ m_textureGathererJob->addDependency(m_syncTextureLoadingJob);
+
+ // Ensures all skeletons are loaded before we try to update them
+ m_updateSkinningPaletteJob->addDependency(m_syncTextureLoadingJob);
+
+ // All world stuff depends on the RenderEntity's localBoundingVolume
+ m_updateLevelOfDetailJob->addDependency(m_updateMeshTriangleListJob);
+ m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob);
+ m_rayCastingJob->addDependency(m_updateMeshTriangleListJob);
+
+ m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob);
+
+ m_filterCompatibleTechniqueJob->setRenderer(this);
+
+ m_defaultRenderStateSet = new RenderStateSet;
+ m_defaultRenderStateSet->addState(RenderStateSet::createState<DepthTest>(GL_LESS));
+ m_defaultRenderStateSet->addState(RenderStateSet::createState<CullFace>(GL_BACK));
+ m_defaultRenderStateSet->addState(RenderStateSet::createState<ColorMask>(true, true, true, true));
+}
+
+Renderer::~Renderer()
+{
+ // If using a threaded rendering approach, tell the thread to exit
+ // and wait for it to be done
+ m_running.fetchAndStoreOrdered(0);
+ if (m_renderThread)
+ m_renderThread->wait();
+
+ delete m_renderQueue;
+ delete m_defaultRenderStateSet;
+ delete m_shaderCache;
+
+ if (!m_ownedContext)
+ QObject::disconnect(m_contextConnection);
+}
+
+void Renderer::dumpInfo() const
+{
+ qDebug() << Q_FUNC_INFO << "t =" << m_time;
+
+ const ShaderManager *shaderManager = m_nodesManager->shaderManager();
+ qDebug() << "=== Shader Manager ===";
+ qDebug() << *shaderManager;
+
+ const TextureManager *textureManager = m_nodesManager->textureManager();
+ qDebug() << "=== Texture Manager ===";
+ qDebug() << *textureManager;
+
+ const TextureImageManager *textureImageManager = m_nodesManager->textureImageManager();
+ qDebug() << "=== Texture Image Manager ===";
+ qDebug() << *textureImageManager;
+}
+
+qint64 Renderer::time() const
+{
+ return m_time;
+}
+
+void Renderer::setTime(qint64 time)
+{
+ m_time = time;
+}
+
+void Renderer::setNodeManagers(NodeManagers *managers)
+{
+ m_nodesManager = managers;
+
+ m_updateShaderDataTransformJob->setManagers(m_nodesManager);
+ m_cleanupJob->setManagers(m_nodesManager);
+ m_calculateBoundingVolumeJob->setManagers(m_nodesManager);
+ m_pickBoundingVolumeJob->setManagers(m_nodesManager);
+ m_rayCastingJob->setManagers(m_nodesManager);
+ m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager());
+ m_sendRenderCaptureJob->setManagers(m_nodesManager);
+ m_sendBufferCaptureJob->setManagers(m_nodesManager);
+ m_updateLevelOfDetailJob->setManagers(m_nodesManager);
+ m_updateSkinningPaletteJob->setManagers(m_nodesManager);
+ m_updateMeshTriangleListJob->setManagers(m_nodesManager);
+ m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
+}
+
+void Renderer::setServices(QServiceLocator *services)
+{
+ m_services = services;
+
+ m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService());
+}
+
+NodeManagers *Renderer::nodeManagers() const
+{
+ return m_nodesManager;
+}
+
+/*!
+ \internal
+
+ Return context which can be used to share resources safely
+ with qt3d main render context.
+*/
+QOpenGLContext *Renderer::shareContext() const
+{
+ QMutexLocker lock(&m_shareContextMutex);
+ return m_shareContext ? m_shareContext
+ : (m_submissionContext->openGLContext()
+ ? m_submissionContext->openGLContext()->shareContext()
+ : nullptr);
+}
+
+// Executed in the command thread
+void Renderer::loadShader(Shader *shader) const
+{
+ Profiling::GLTimeRecorder recorder(Profiling::ShaderUpload);
+ LoadShaderCommand cmd(shader);
+ m_commandThread->executeCommand(&cmd);
+}
+
+void Renderer::setOpenGLContext(QOpenGLContext *context)
+{
+ m_glContext = context;
+}
+
+// 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()
+{
+ m_submissionContext.reset(new SubmissionContext);
+ m_submissionContext->setRenderer(this);
+
+ QOpenGLContext* ctx = m_glContext;
+
+ {
+ QMutexLocker lock(&m_shareContextMutex);
+ // If we are using our own context (not provided by QtQuick),
+ // we need to create it
+ if (!m_glContext) {
+ ctx = new QOpenGLContext;
+ ctx->setShareContext(qt_gl_global_share_context());
+
+ // TO DO: Shouldn't we use the highest context available and trust
+ // QOpenGLContext to fall back on the best lowest supported ?
+ const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING");
+
+ if (!debugLoggingMode.isEmpty()) {
+ QSurfaceFormat sf = ctx->format();
+ sf.setOption(QSurfaceFormat::DebugContext);
+ ctx->setFormat(sf);
+ }
+
+ // Create OpenGL context
+ if (ctx->create())
+ qCDebug(Backend) << "OpenGL context created with actual format" << ctx->format();
+ else
+ qCWarning(Backend) << Q_FUNC_INFO << "OpenGL context creation failed";
+ m_ownedContext = true;
+ } else {
+ // Context is not owned by us, so we need to know if it gets destroyed
+ m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed,
+ [this] { releaseGraphicsResources(); });
+ }
+
+ if (!ctx->shareContext()) {
+ m_shareContext = new QOpenGLContext;
+ m_shareContext->setFormat(ctx->format());
+ m_shareContext->setShareContext(ctx);
+ m_shareContext->create();
+ }
+
+ // Set shader cache on submission context and command thread
+ m_submissionContext->setShaderCache(m_shaderCache);
+ m_commandThread->setShaderCache(m_shaderCache);
+
+ // Note: we don't have a surface at this point
+ // The context will be made current later on (at render time)
+ m_submissionContext->setOpenGLContext(ctx);
+
+ // Store the format used by the context and queue up creating an
+ // offscreen surface in the main thread so that it is available
+ // for use when we want to shutdown the renderer. We need to create
+ // the offscreen surface on the main thread because on some platforms
+ // (MS Windows), an offscreen surface is just a hidden QWindow.
+ m_format = ctx->format();
+ QMetaObject::invokeMethod(m_offscreenHelper, "createOffscreenSurface");
+
+ // Initialize command thread (uses the offscreen surface to make its own ctx current)
+ m_commandThread->initialize(ctx, m_offscreenHelper);
+ // Note: the offscreen surface is also used at shutdown time to release resources
+ // of the submission gl context (when the window is already gone).
+ // By that time (in releaseGraphicResources), the commandThread has been destroyed
+ // and the offscreenSurface can be reused
+ }
+
+ // Awake setScenegraphRoot in case it was waiting
+ m_waitForInitializationToBeCompleted.release(1);
+ // Allow the aspect manager to proceed
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+}
+
+/*!
+ * \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()
+{
+ qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer shutdown";
+ m_running.store(0);
+
+ // We delete any renderqueue that we may not have had time to render
+ // before the surface was destroyed
+ qDeleteAll(m_renderQueue->nextFrameQueue());
+ m_renderQueue->reset();
+
+ m_commandThread->shutdown();
+
+ 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();
+ }
+}
+
+/*!
+ \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";
+ return;
+ }
+
+ QOpenGLContext *context = m_submissionContext->openGLContext();
+ Q_ASSERT(context);
+ if (context->makeCurrent(offscreenSurface)) {
+
+ // Clean up the graphics context and any resources
+ const QVector<GLTexture*> activeTextures = m_nodesManager->glTextureManager()->activeResources();
+ for (GLTexture *tex : activeTextures)
+ tex->destroyGLTexture();
+
+ // Do the same thing with buffers
+ const QVector<HGLBuffer> activeBuffers = m_nodesManager->glBufferManager()->activeHandles();
+ for (const HGLBuffer &bufferHandle : activeBuffers) {
+ GLBuffer *buffer = m_nodesManager->glBufferManager()->data(bufferHandle);
+ buffer->destroy(m_submissionContext.data());
+ }
+
+ // Do the same thing with VAOs
+ const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles();
+ for (const HVao &vaoHandle : activeVaos) {
+ OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle);
+ vao->destroy();
+ }
+
+ context->doneCurrent();
+ } else {
+ qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
+ }
+
+ if (m_ownedContext)
+ delete context;
+ if (m_shareContext)
+ delete m_shareContext;
+
+ m_submissionContext.reset(nullptr);
+ qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown";
+}
+
+void Renderer::setSurfaceExposed(bool exposed)
+{
+ qCDebug(Backend) << "Window exposed: " << exposed;
+ m_exposed.fetchAndStoreOrdered(exposed);
+}
+
+Render::FrameGraphNode *Renderer::frameGraphRoot() const
+{
+ Q_ASSERT(m_settings);
+ if (m_nodesManager && m_nodesManager->frameGraphManager() && m_settings)
+ return m_nodesManager->frameGraphManager()->lookupNode(m_settings->activeFrameGraphID());
+ return nullptr;
+}
+
+// QAspectThread context
+// Order of execution :
+// 1) RenderThread is created -> release 1 of m_waitForInitializationToBeCompleted when started
+// 2) setSceneRoot waits to acquire initialization
+// 3) submitRenderView -> check for surface
+// -> make surface current + create proper glHelper if needed
+void Renderer::setSceneRoot(QBackendNodeFactory *factory, Entity *sgRoot)
+{
+ Q_ASSERT(sgRoot);
+ Q_UNUSED(factory);
+
+ // If initialization hasn't been completed we must wait
+ m_waitForInitializationToBeCompleted.acquire();
+
+ m_renderSceneRoot = sgRoot;
+ if (!m_renderSceneRoot)
+ qCWarning(Backend) << "Failed to build render scene";
+ m_renderSceneRoot->dump();
+ qCDebug(Backend) << Q_FUNC_INFO << "DUMPING SCENE";
+
+ // Set the scene root on the jobs
+ m_worldTransformJob->setRoot(m_renderSceneRoot);
+ m_expandBoundingVolumeJob->setRoot(m_renderSceneRoot);
+ m_calculateBoundingVolumeJob->setRoot(m_renderSceneRoot);
+ m_cleanupJob->setRoot(m_renderSceneRoot);
+ m_pickBoundingVolumeJob->setRoot(m_renderSceneRoot);
+ m_rayCastingJob->setRoot(m_renderSceneRoot);
+ m_updateLevelOfDetailJob->setRoot(m_renderSceneRoot);
+ m_updateSkinningPaletteJob->setRoot(m_renderSceneRoot);
+ m_updateTreeEnabledJob->setRoot(m_renderSceneRoot);
+
+ // Set all flags to dirty
+ m_dirtyBits.marked |= AbstractRenderer::AllDirty;
+}
+
+void Renderer::registerEventFilter(QEventFilterService *service)
+{
+ qCDebug(Backend) << Q_FUNC_INFO << QThread::currentThread();
+ service->registerEventFilter(m_pickEventFilter.data(), 1024);
+}
+
+void Renderer::setSettings(RenderSettings *settings)
+{
+ m_settings = settings;
+}
+
+RenderSettings *Renderer::settings() const
+{
+ return m_settings;
+}
+
+void Renderer::render()
+{
+ // Traversing the framegraph tree from root to lead node
+ // Allows us to define the rendering set up
+ // Camera, RenderTarget ...
+
+ // Utimately the renderer should be a framework
+ // For the processing of the list of renderviews
+
+ // Matrice update, bounding volumes computation ...
+ // Should be jobs
+
+ // namespace Qt3DCore has 2 distincts node trees
+ // One scene description
+ // One framegraph description
+
+ while (m_running.load() > 0) {
+ doRender();
+ // TO DO: Restore windows exposed detection
+ // Probably needs to happens some place else though
+ }
+}
+
+void Renderer::doRender(bool scene3dBlocking)
+{
+ Renderer::ViewSubmissionResultData submissionData;
+ bool hasCleanedQueueAndProceeded = false;
+ bool preprocessingComplete = false;
+ bool beganDrawing = false;
+ const bool canSubmit = isReadyToSubmit();
+
+ // Lock the mutex to protect access to the renderQueue while we look for its state
+ QMutexLocker locker(m_renderQueue->mutex());
+ bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
+
+ // Scene3D Blocking Mode
+ if (scene3dBlocking && !queueIsComplete && !queueIsEmpty) {
+ int i = 0;
+ // We wait at most 10ms to avoid a case we could never recover from
+ while (!queueIsComplete && i++ < 10) {
+ QThread::msleep(1);
+ qCDebug(Backend) << Q_FUNC_INFO << "Waiting for ready queue (try:" << i << "/ 10)";
+ locker.unlock();
+ queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ locker.relock();
+ }
+ }
+
+ // When using synchronous rendering (QtQuick)
+ // We are not sure that the frame queue is actually complete
+ // Since a call to render may not be synched with the completions
+ // of the RenderViewJobs
+ // In such a case we return early, waiting for a next call with
+ // the frame queue complete at this point
+
+ // RenderQueue is complete (but that means it may be of size 0)
+ if (canSubmit && (queueIsComplete && !queueIsEmpty)) {
+ const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue();
+
+#if QT_CONFIG(qt3d_profile_jobs)
+ // Save start of frame
+ JobRunStats submissionStatsPart1;
+ JobRunStats submissionStatsPart2;
+ submissionStatsPart1.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart1;
+ submissionStatsPart1.jobId.typeAndInstance[1] = 0;
+ submissionStatsPart1.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
+ submissionStatsPart1.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
+ submissionStatsPart2.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart2;
+ submissionStatsPart2.jobId.typeAndInstance[1] = 0;
+ submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
+#endif
+
+ if (canRender()) {
+ { // Scoped to destroy surfaceLock
+ QSurface *surface = nullptr;
+ for (const Render::RenderView *rv: renderViews) {
+ surface = rv->surface();
+ if (surface)
+ break;
+ }
+
+ SurfaceLocker surfaceLock(surface);
+ const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid());
+ if (surfaceIsValid) {
+ // Reset state for each draw if we don't have complete control of the context
+ if (!m_ownedContext)
+ m_submissionContext->setCurrentStateSet(nullptr);
+ beganDrawing = m_submissionContext->beginDrawing(surface);
+ if (beganDrawing) {
+ // 1) Execute commands for buffer uploads, texture updates, shader loading first
+ updateGLResources();
+ // 2) Update VAO and copy data into commands to allow concurrent submission
+ prepareCommandsSubmission(renderViews);
+ preprocessingComplete = true;
+ }
+ }
+ }
+ // 2) Proceed to next frame and start preparing frame n + 1
+ m_renderQueue->reset();
+ locker.unlock(); // Done protecting RenderQueue
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ hasCleanedQueueAndProceeded = true;
+
+#if QT_CONFIG(qt3d_profile_jobs)
+ if (preprocessingComplete) {
+ submissionStatsPart2.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
+ submissionStatsPart1.endTime = submissionStatsPart2.startTime;
+ }
+#endif
+ // Only try to submit the RenderViews if the preprocessing was successful
+ // This part of the submission is happening in parallel to the RV building for the next frame
+ if (preprocessingComplete) {
+ // 3) Submit the render commands for frame n (making sure we never reference something that could be changing)
+ // Render using current device state and renderer configuration
+ submissionData = submitRenderViews(renderViews);
+
+ // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader deleted...)
+ cleanGraphicsResources();
+ }
+ }
+
+#if QT_CONFIG(qt3d_profile_jobs)
+ // Execute the pending shell commands
+ m_commandExecuter->performAsynchronousCommandExecution(renderViews);
+#endif
+
+ // Delete all the RenderViews which will clear the allocators
+ // that were used for their allocation
+ qDeleteAll(renderViews);
+
+#if QT_CONFIG(qt3d_profile_jobs)
+ if (preprocessingComplete) {
+ // Save submission elapsed time
+ submissionStatsPart2.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed();
+ // Note this is safe since proceedToNextFrame is the one going to trigger
+ // the write to the file, and this is performed after this step
+ Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart1);
+ Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart2);
+ Profiling::GLTimeRecorder::writeResults();
+ }
+#endif
+ }
+
+ // Only reset renderQueue and proceed to next frame if the submission
+ // succeeded or if we are using a render thread and that is wasn't performed
+ // already
+
+ // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong
+ // with the rendering and/or the renderqueue is incomplete from some reason
+ // (in the case of scene3d the render jobs may be taking too long ....)
+ // or alternatively it could be complete but empty (RenderQueue of size 0)
+ if (!hasCleanedQueueAndProceeded &&
+ (m_renderThread || queueIsComplete || queueIsEmpty)) {
+ // RenderQueue was full but something bad happened when
+ // trying to render it and therefore proceedToNextFrame was not called
+ // Note: in this case the renderQueue mutex is still locked
+
+ // Reset the m_renderQueue so that we won't try to render
+ // with a queue used by a previous frame with corrupted content
+ // if the current queue was correctly submitted
+ m_renderQueue->reset();
+
+ // We allow the RenderTickClock service to proceed to the next frame
+ // In turn this will allow the aspect manager to request a new set of jobs
+ // to be performed for each aspect
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ }
+
+ // Perform the last swapBuffers calls after the proceedToNextFrame
+ // as this allows us to gain a bit of time for the preparation of the
+ // next frame
+ // Finish up with last surface used in the list of RenderViews
+ if (beganDrawing) {
+ SurfaceLocker surfaceLock(submissionData.surface);
+ // Finish up with last surface used in the list of RenderViews
+ m_submissionContext->endDrawing(submissionData.lastBoundFBOId == m_submissionContext->defaultFBO() && surfaceLock.isSurfaceValid());
+ }
+}
+
+// Called by RenderViewJobs
+// When the frameQueue is complete and we are using a renderThread
+// we allow the render thread to proceed
+void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder)
+{
+ QMutexLocker locker(m_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.load())
+ Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0);
+ m_submitRenderViewsSemaphore.release(1);
+ }
+}
+
+bool Renderer::canRender() const
+{
+ // Make sure that we've not been told to terminate
+ if (m_renderThread && !m_running.load()) {
+ qCDebug(Rendering) << "RenderThread termination requested whilst waiting";
+ return false;
+ }
+
+ // TO DO: Check if all surfaces have been destroyed...
+ // It may be better if the last window to be closed trigger a call to shutdown
+ // Rather than having checks for the surface everywhere
+
+ return true;
+}
+
+bool Renderer::isReadyToSubmit()
+{
+ // If we are using a render thread, make sure that
+ // we've been told to render before rendering
+ if (m_renderThread) { // Prevent ouf of order execution
+ m_submitRenderViewsSemaphore.acquire(1);
+
+ // Check if shutdown has been requested
+ if (m_running.load() == 0)
+ return false;
+
+ // When using Thread rendering, the semaphore should only
+ // be released when the frame queue is complete and there's
+ // something to render
+ // The case of shutdown should have been handled just before
+ Q_ASSERT(m_renderQueue->isFrameQueueComplete());
+ }
+ return true;
+}
+
+// Main thread
+QVariant Renderer::executeCommand(const QStringList &args)
+{
+#if QT_CONFIG(qt3d_profile_jobs)
+ return m_commandExecuter->executeCommand(args);
+#else
+ Q_UNUSED(args);
+#endif
+ return QVariant();
+}
+
+/*!
+ \internal
+ Called in the context of the aspect thread from QRenderAspect::onRegistered
+*/
+void Renderer::setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper)
+{
+ QMutexLocker locker(&m_offscreenSurfaceMutex);
+ m_offscreenHelper = helper;
+}
+
+QSurfaceFormat Renderer::format()
+{
+ return m_format;
+}
+
+// When this function is called, we must not be processing the commands for frame n+1
+void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderViews)
+{
+ OpenGLVertexArrayObject *vao = nullptr;
+ QHash<HVao, bool> updatedTable;
+
+ for (RenderView *rv: renderViews) {
+ const QVector<RenderCommand *> commands = rv->commands();
+ for (RenderCommand *command : commands) {
+ // Update/Create VAO
+ if (command->m_type == RenderCommand::Draw) {
+ Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command->m_geometry);
+ GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command->m_geometryRenderer);
+ Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command->m_shader);
+
+ // We should never have inserted a command for which these are null
+ // in the first place
+ Q_ASSERT(rGeometry && rGeometryRenderer && shader);
+
+ // The VAO should be created only once for a QGeometry and a ShaderProgram
+ // Manager should have a VAO Manager that are indexed by QMeshData and Shader
+ // RenderCommand should have a handle to the corresponding VAO for the Mesh and Shader
+ HVao vaoHandle;
+
+ // Create VAO or return already created instance associated with command shader/geometry
+ // (VAO is emulated if not supported)
+ createOrUpdateVAO(command, &vaoHandle, &vao);
+ command->m_vao = vaoHandle;
+
+ // Avoids redoing the same thing for the same VAO
+ if (!updatedTable.contains(vaoHandle)) {
+ updatedTable.insert(vaoHandle, true);
+
+ // Do we have any attributes that are dirty ?
+ const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, command);
+
+ // If true, we need to reupload all attributes to set the VAO
+ // Otherwise only dirty attributes will be updates
+ const bool requiresFullVAOUpdate = (!vao->isSpecified()) || (rGeometry->isDirty() || rGeometryRenderer->isDirty());
+
+ // Append dirty Geometry to temporary vector
+ // so that its dirtiness can be unset later
+ if (rGeometry->isDirty())
+ m_dirtyGeometry.push_back(rGeometry);
+
+ if (!command->m_attributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) {
+ Profiling::GLTimeRecorder recorder(Profiling::VAOUpload);
+ // Activate shader
+ m_submissionContext->activateShader(shader->dna());
+ // Bind VAO
+ vao->bind();
+ // Update or set Attributes and Buffers for the given rGeometry and Command
+ // Note: this fills m_dirtyAttributes as well
+ if (updateVAOWithAttributes(rGeometry, command, shader, requiresFullVAOUpdate))
+ vao->setSpecified(true);
+ }
+ }
+
+ // Unset dirtiness on rGeometryRenderer only
+ // The rGeometry may be shared by several rGeometryRenderer
+ // so we cannot unset its dirtiness at this point
+ if (rGeometryRenderer->isDirty())
+ rGeometryRenderer->unsetDirty();
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ shader->prepareUniforms(command->m_parameterPack);
+
+ // TO DO: The step below could be performed by the RenderCommand builder job
+ { // Scoped to show extent
+ command->m_isValid = !command->m_attributes.empty();
+ if (!command->m_isValid)
+ continue;
+
+ // Update the draw command with what's going to be needed for the drawing
+ uint primitiveCount = rGeometryRenderer->vertexCount();
+ uint estimatedCount = 0;
+ Attribute *indexAttribute = nullptr;
+ Attribute *indirectAttribute = nullptr;
+
+ const QVector<Qt3DCore::QNodeId> attributeIds = rGeometry->attributes();
+ for (Qt3DCore::QNodeId attributeId : attributeIds) {
+ Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId);
+ switch (attribute->attributeType()) {
+ case QAttribute::IndexAttribute:
+ indexAttribute = attribute;
+ break;
+ case QAttribute::DrawIndirectAttribute:
+ indirectAttribute = attribute;
+ break;
+ case QAttribute::VertexAttribute: {
+ if (command->m_attributes.contains(attribute->nameId()))
+ estimatedCount = qMax(attribute->count(), estimatedCount);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ command->m_drawIndexed = (indexAttribute != nullptr);
+ command->m_drawIndirect = (indirectAttribute != nullptr);
+
+ // Update the draw command with all the information required for the drawing
+ if (command->m_drawIndexed) {
+ command->m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType());
+ command->m_indexAttributeByteOffset = indexAttribute->byteOffset() + rGeometryRenderer->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_nodesManager->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 = rGeometryRenderer->primitiveType();
+ command->m_primitiveRestartEnabled = rGeometryRenderer->primitiveRestartEnabled();
+ command->m_restartIndexValue = rGeometryRenderer->restartIndexValue();
+ command->m_firstInstance = rGeometryRenderer->firstInstance();
+ command->m_instanceCount = rGeometryRenderer->instanceCount();
+ command->m_firstVertex = rGeometryRenderer->firstVertex();
+ command->m_indexOffset = rGeometryRenderer->indexOffset();
+ command->m_verticesPerPatch = rGeometryRenderer->verticesPerPatch();
+ } // scope
+ } else if (command->m_type == RenderCommand::Compute) {
+ Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command->m_shader);
+ Q_ASSERT(shader);
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ shader->prepareUniforms(command->m_parameterPack);
+ }
+ }
+ }
+
+ // Make sure we leave nothing bound
+ if (vao)
+ vao->release();
+
+ // Unset dirtiness on Geometry and Attributes
+ // Note: we cannot do it in the loop above as we want to be sure that all
+ // the VAO which reference the geometry/attributes are properly updated
+ for (Attribute *attribute : qAsConst(m_dirtyAttributes))
+ attribute->unsetDirty();
+ m_dirtyAttributes.clear();
+
+ for (Geometry *geometry : qAsConst(m_dirtyGeometry))
+ geometry->unsetDirty();
+ m_dirtyGeometry.clear();
+}
+
+// Executed in a job
+void Renderer::lookForAbandonedVaos()
+{
+ const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles();
+ for (const HVao &handle : activeVaos) {
+ OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(handle);
+
+ // Make sure to only mark VAOs for deletion that were already created
+ // (ignore those that might be currently under construction in the render thread)
+ if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_nodesManager->shaderManager())) {
+ m_abandonedVaosMutex.lock();
+ m_abandonedVaos.push_back(handle);
+ m_abandonedVaosMutex.unlock();
+ }
+ }
+}
+
+// Executed in a job
+void Renderer::lookForDirtyBuffers()
+{
+ const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
+ for (const HBuffer &handle: activeBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ if (buffer->isDirty())
+ m_dirtyBuffers.push_back(handle);
+ }
+}
+
+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(handle);
+ }
+}
+
+// Executed in a job
+void Renderer::lookForDirtyTextures()
+{
+ const QVector<HTexture> activeTextureHandles = m_nodesManager->textureManager()->activeHandles();
+ for (const HTexture &handle: activeTextureHandles) {
+ Texture *texture = m_nodesManager->textureManager()->data(handle);
+ // Dirty meaning that something has changed on the texture
+ // either properties, parameters, generator or a texture image
+ if (texture->dirtyFlags() != Texture::NotDirty)
+ m_dirtyTextures.push_back(handle);
+ }
+}
+
+// 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 <= ShaderBuilder::Compute; i++) {
+ const auto builderType = static_cast<ShaderBuilder::ShaderType>(i);
+ if (!shaderBuilder->shaderGraph(builderType).isValid())
+ continue;
+
+ if (shaderBuilder->isShaderCodeDirty(builderType)) {
+ shaderBuilder->generateCode(builderType);
+ }
+
+ QShaderProgram::ShaderType shaderType = QShaderProgram::Vertex;
+ switch (builderType) {
+ case ShaderBuilder::Vertex:
+ shaderType = QShaderProgram::Vertex;
+ break;
+ case ShaderBuilder::TessellationControl:
+ shaderType = QShaderProgram::TessellationControl;
+ break;
+ case ShaderBuilder::TessellationEvaluation:
+ shaderType = QShaderProgram::TessellationEvaluation;
+ break;
+ case ShaderBuilder::Geometry:
+ shaderType = QShaderProgram::Geometry;
+ break;
+ case ShaderBuilder::Fragment:
+ shaderType = QShaderProgram::Fragment;
+ break;
+ case ShaderBuilder::Compute:
+ shaderType = QShaderProgram::Compute;
+ break;
+ }
+
+ const auto code = shaderBuilder->shaderCode(builderType);
+ shader->setShaderCode(shaderType, code);
+ }
+ }
+
+ if (Q_UNLIKELY(shader->hasPendingNotifications()))
+ shader->submitPendingNotifications();
+ // If the shader hasn't be loaded, load it
+ if (shader != nullptr && !shader->isLoaded())
+ loadShader(shader);
+ }
+ }
+ }
+}
+
+// Render Thread
+void Renderer::updateGLResources()
+{
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::BufferUpload);
+ const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers);
+ for (const HBuffer &handle: dirtyBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ // Forces creation if it doesn't exit
+ // Also note the binding point doesn't really matter here, we just upload data
+ if (!m_submissionContext->hasGLBufferForBuffer(buffer))
+ m_submissionContext->glBufferForRenderBuffer(buffer, GLBuffer::ArrayBuffer);
+ // Update the glBuffer data
+ m_submissionContext->updateBuffer(buffer);
+ buffer->unsetDirty();
+ }
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::TextureUpload);
+ const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures);
+ for (const HTexture &handle: activeTextureHandles) {
+ Texture *texture = m_nodesManager->textureManager()->data(handle);
+ // Upload/Update texture
+ updateTexture(texture);
+ }
+ }
+ // When Textures are cleaned up, their id is saved
+ // so that they can be cleaned up in the render thread
+ // Note: we perform this step in second so that the previous updateTexture call
+ // has a chance to find a shared texture
+ const QVector<Qt3DCore::QNodeId> cleanedUpTextureIds = m_nodesManager->textureManager()->takeTexturesIdsToCleanup();
+ for (const Qt3DCore::QNodeId textureCleanedUpId: cleanedUpTextureIds) {
+ cleanupTexture(m_nodesManager->textureManager()->lookupResource(textureCleanedUpId));
+ // We can really release the texture at this point
+ m_nodesManager->textureManager()->releaseResource(textureCleanedUpId);
+ }
+}
+
+// Render Thread
+void Renderer::updateTexture(Texture *texture)
+{
+ // Check that the current texture images are still in place, if not, do not update
+ const bool isValid = texture->isValid();
+ if (!isValid)
+ return;
+
+ // For implementing unique, non-shared, non-cached textures.
+ // for now, every texture is shared by default
+
+ bool isUnique = false;
+
+ // TO DO: Update the vector once per frame (or in a job)
+ const QVector<HAttachment> activeRenderTargetOutputs = m_nodesManager->attachmentManager()->activeHandles();
+ // A texture is unique if it's being reference by a render target output
+ for (const HAttachment &attachmentHandle : activeRenderTargetOutputs) {
+ RenderTargetOutput *attachment = m_nodesManager->attachmentManager()->data(attachmentHandle);
+ if (attachment->textureUuid() == texture->peerId()) {
+ isUnique = true;
+ break;
+ }
+ }
+
+ // Try to find the associated GLTexture for the backend Texture
+ GLTextureManager *glTextureManager = m_nodesManager->glTextureManager();
+ GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId());
+
+ // No GLTexture associated yet -> create it
+ if (glTexture == nullptr) {
+ if (isUnique)
+ glTextureManager->createUnique(texture);
+ else
+ glTextureManager->getOrCreateShared(texture);
+ texture->unsetDirty();
+ return;
+ }
+
+ // if this texture is a shared texture, we might need to look for a new TextureImpl
+ // and abandon the old one
+ if (glTextureManager->isShared(glTexture)) {
+ glTextureManager->abandon(glTexture, texture);
+ // Check if a shared texture should become unique
+ if (isUnique)
+ glTextureManager->createUnique(texture);
+ else
+ glTextureManager->getOrCreateShared(texture);
+ texture->unsetDirty();
+ return;
+ }
+
+ // this texture node is the only one referring to the GLTexture.
+ // we could thus directly modify the texture. Instead, for non-unique textures,
+ // we first see if there is already a matching texture present.
+ if (!isUnique) {
+ GLTexture *newSharedTex = glTextureManager->findMatchingShared(texture);
+ if (newSharedTex && newSharedTex != glTexture) {
+ glTextureManager->abandon(glTexture, texture);
+ glTextureManager->adoptShared(newSharedTex, texture);
+ texture->unsetDirty();
+ return;
+ }
+ }
+
+ // we hold a reference to a unique or exclusive access to a shared texture
+ // we can thus modify the texture directly.
+ const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags();
+
+ if (dirtyFlags.testFlag(Texture::DirtyProperties) &&
+ !glTextureManager->setProperties(glTexture, texture->properties()))
+ qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setProperties failed, should be non-shared";
+
+ if (dirtyFlags.testFlag(Texture::DirtyParameters) &&
+ !glTextureManager->setParameters(glTexture, texture->parameters()))
+ qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setParameters failed, should be non-shared";
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyImageGenerators) &&
+ !glTextureManager->setImages(glTexture, texture->textureImages()))
+ qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerators failed, should be non-shared";
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyDataGenerator) &&
+ !glTextureManager->setGenerator(glTexture, texture->dataGenerator()))
+ qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerator failed, should be non-shared";
+
+ // Unset the dirty flag on the texture
+ texture->unsetDirty();
+}
+
+// Render Thread
+void Renderer::cleanupTexture(const Texture *texture)
+{
+ GLTextureManager *glTextureManager = m_nodesManager->glTextureManager();
+ GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId());
+
+ if (glTexture != nullptr)
+ glTextureManager->abandon(glTexture, texture);
+}
+
+void Renderer::downloadGLBuffers()
+{
+ lookForDownloadableBuffers();
+ const QVector<HBuffer> downloadableHandles = std::move(m_downloadableBuffers);
+ for (const HBuffer &handle : downloadableHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ QByteArray content = m_submissionContext->downloadBufferContent(buffer);
+ m_sendBufferCaptureJob->addRequest(QPair<Buffer*, QByteArray>(buffer, content));
+ }
+}
+
+// Happens in RenderThread context when all RenderViewJobs are done
+// Returns the id of the last bound FBO
+Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Render::RenderView *> &renderViews)
+{
+ QElapsedTimer timer;
+ quint64 queueElapsed = 0;
+ timer.start();
+
+ const int renderViewsCount = renderViews.size();
+ quint64 frameElapsed = queueElapsed;
+ m_lastFrameCorrect.store(1); // everything fine until now.....
+
+ qCDebug(Memory) << Q_FUNC_INFO << "rendering frame ";
+
+ // We might not want to render on the default FBO
+ uint lastBoundFBOId = m_submissionContext->boundFrameBufferObject();
+ QSurface *surface = nullptr;
+ QSurface *previousSurface = nullptr;
+ for (const Render::RenderView *rv: renderViews) {
+ previousSurface = rv->surface();
+ if (previousSurface)
+ break;
+ }
+ QSurface *lastUsedSurface = nullptr;
+
+ for (int i = 0; i < renderViewsCount; ++i) {
+ // Initialize GraphicsContext for drawing
+ // If the RenderView has a RenderStateSet defined
+ const RenderView *renderView = renderViews.at(i);
+
+ // Check if using the same surface as the previous RenderView.
+ // If not, we have to free up the context from the previous surface
+ // and make the context current on the new surface
+ surface = renderView->surface();
+ SurfaceLocker surfaceLock(surface);
+
+ // TO DO: Make sure that the surface we are rendering too has not been unset
+
+ // For now, if we do not have a surface, skip this renderview
+ // TODO: Investigate if it's worth providing a fallback offscreen surface
+ // to use when surface is null. Or if we should instead expose an
+ // offscreensurface to Qt3D.
+ if (!surface || !surfaceLock.isSurfaceValid()) {
+ m_lastFrameCorrect.store(0);
+ continue;
+ }
+
+ lastUsedSurface = surface;
+ const bool surfaceHasChanged = surface != previousSurface;
+
+ if (surfaceHasChanged && previousSurface) {
+ const bool swapBuffers = (lastBoundFBOId == m_submissionContext->defaultFBO()) && PlatformSurfaceFilter::isSurfaceValid(previousSurface);
+ // 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.store(0);
+ continue;
+ }
+
+ previousSurface = surface;
+ lastBoundFBOId = m_submissionContext->boundFrameBufferObject();
+ }
+
+ // Apply Memory Barrier if needed
+ if (renderView->memoryBarrier() != QMemoryBarrier::None)
+ m_submissionContext->memoryBarrier(renderView->memoryBarrier());
+
+ // Note: the RenderStateSet is allocated once per RV if needed
+ // and it contains a list of StateVariant value types
+ RenderStateSet *renderViewStateSet = renderView->stateSet();
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::StateUpdate);
+ // Set the RV state if not null,
+ if (renderViewStateSet != nullptr)
+ m_submissionContext->setCurrentStateSet(renderViewStateSet);
+ else
+ m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet);
+ }
+
+ // Set RenderTarget ...
+ // Activate RenderTarget
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::RenderTargetUpdate);
+ m_submissionContext->activateRenderTarget(renderView->renderTargetId(),
+ renderView->attachmentPack(),
+ lastBoundFBOId);
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::ClearBuffer);
+ // set color, depth, stencil clear values (only if needed)
+ auto clearBufferTypes = renderView->clearTypes();
+ if (clearBufferTypes & QClearBuffers::ColorBuffer) {
+ const QVector4D vCol = renderView->globalClearColorBufferInfo().clearColor;
+ m_submissionContext->clearColor(QColor::fromRgbF(vCol.x(), vCol.y(), vCol.z(), vCol.w()));
+ }
+ if (clearBufferTypes & QClearBuffers::DepthBuffer)
+ m_submissionContext->clearDepthValue(renderView->clearDepthValue());
+ if (clearBufferTypes & QClearBuffers::StencilBuffer)
+ m_submissionContext->clearStencilValue(renderView->clearStencilValue());
+
+ // Clear BackBuffer
+ m_submissionContext->clearBackBuffer(clearBufferTypes);
+
+ // if there are ClearColors set for different draw buffers,
+ // clear each of these draw buffers individually now
+ const QVector<ClearBufferInfo> clearDrawBuffers = renderView->specificClearColorBufferInfo();
+ for (const ClearBufferInfo &clearBuffer : clearDrawBuffers)
+ m_submissionContext->clearBufferf(clearBuffer.drawBufferIndex, clearBuffer.clearColor);
+ }
+
+ // Set the Viewport
+ m_submissionContext->setViewport(renderView->viewport(), renderView->surfaceSize() * renderView->devicePixelRatio());
+
+ // Execute the render commands
+ if (!executeCommandsSubmission(renderView))
+ m_lastFrameCorrect.store(0); // something went wrong; make sure to render the next frame!
+
+ // executeCommandsSubmission takes care of restoring the stateset to the value
+ // of gc->currentContext() at the moment it was called (either
+ // renderViewStateSet or m_defaultRenderStateSet)
+ if (!renderView->renderCaptureNodeId().isNull()) {
+ const QRenderCaptureRequest request = renderView->renderCaptureRequest();
+ const QSize size = m_submissionContext->renderTargetSize(renderView->surfaceSize() * renderView->devicePixelRatio());
+ 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);
+ addRenderCaptureSendRequest(renderView->renderCaptureNodeId());
+ }
+
+ if (renderView->isDownloadBuffersEnable())
+ downloadGLBuffers();
+
+ // Perform BlitFramebuffer operations
+ if (renderView->hasBlitFramebufferInfo()) {
+ const auto &blitFramebufferInfo = renderView->blitFrameBufferInfo();
+ const QNodeId inputTargetId = blitFramebufferInfo.sourceRenderTargetId;
+ const QNodeId outputTargetId = blitFramebufferInfo.destinationRenderTargetId;
+ const QRect inputRect = blitFramebufferInfo.sourceRect;
+ const QRect outputRect = blitFramebufferInfo.destinationRect;
+ const QRenderTargetOutput::AttachmentPoint inputAttachmentPoint = blitFramebufferInfo.sourceAttachmentPoint;
+ const QRenderTargetOutput::AttachmentPoint outputAttachmentPoint = blitFramebufferInfo.destinationAttachmentPoint;
+ const QBlitFramebuffer::InterpolationMethod interpolationMethod = blitFramebufferInfo.interpolationMethod;
+ m_submissionContext->blitFramebuffer(inputTargetId, outputTargetId, inputRect, outputRect, lastBoundFBOId,
+ inputAttachmentPoint, outputAttachmentPoint,
+ interpolationMethod);
+ }
+
+
+ frameElapsed = timer.elapsed() - frameElapsed;
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submitted Renderview " << i + 1 << "/" << renderViewsCount << "in " << frameElapsed << "ms";
+ frameElapsed = timer.elapsed();
+ }
+
+ // Bind lastBoundFBOId back. Needed also in threaded mode.
+ // lastBoundFBOId != m_graphicsContext->activeFBO() when the last FrameGraph leaf node/renderView
+ // contains RenderTargetSelector/RenderTarget
+ if (lastBoundFBOId != m_submissionContext->activeFBO())
+ m_submissionContext->bindFramebuffer(lastBoundFBOId, GraphicsHelperInterface::FBOReadAndDraw);
+
+ // Reset state and call doneCurrent if the surface
+ // is valid and was actually activated
+ if (surface && m_submissionContext->hasValidGLHelper()) {
+ // Reset state to the default state if the last stateset is not the
+ // defaultRenderStateSet
+ if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet)
+ m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet);
+ }
+
+ queueElapsed = timer.elapsed() - queueElapsed;
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submission of Queue in " << queueElapsed << "ms <=> " << queueElapsed / renderViewsCount << "ms per RenderView <=> Avg " << 1000.0f / (queueElapsed * 1.0f/ renderViewsCount * 1.0f) << " RenderView/s";
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submission Completed in " << timer.elapsed() << "ms";
+
+ // Stores the necessary information to safely perform
+ // the last swap buffer call
+ ViewSubmissionResultData resultData;
+ resultData.lastBoundFBOId = lastBoundFBOId;
+ resultData.surface = lastUsedSurface;
+
+ return resultData;
+}
+
+void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node)
+{
+ Q_UNUSED(node);
+ m_dirtyBits.marked |= changes;
+}
+
+Renderer::BackendNodeDirtySet Renderer::dirtyBits()
+{
+ return m_dirtyBits.marked;
+}
+
+#if defined(QT_BUILD_INTERNAL)
+void Renderer::clearDirtyBits(BackendNodeDirtySet changes)
+{
+ m_dirtyBits.remaining &= ~changes;
+ m_dirtyBits.marked &= ~changes;
+}
+#endif
+
+bool Renderer::shouldRender()
+{
+ // 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.load());
+}
+
+void Renderer::skipNextFrame()
+{
+ Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always);
+
+ // make submitRenderViews() actually run
+ m_renderQueue->setNoRender();
+ m_submitRenderViewsSemaphore.release(1);
+}
+
+// Waits to be told to create jobs for the next frame
+// Called by QRenderAspect jobsToExecute context of QAspectThread
+// Returns all the jobs (and with proper dependency chain) required
+// for the rendering of the scene
+QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
+{
+ QVector<QAspectJobPtr> renderBinJobs;
+
+ // Create the jobs to build the frame
+ const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs();
+
+ // Remove previous dependencies
+ m_calculateBoundingVolumeJob->removeDependency(QWeakPointer<QAspectJob>());
+ m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>());
+
+ // Set dependencies
+ for (const QAspectJobPtr &bufferJob : bufferJobs)
+ m_calculateBoundingVolumeJob->addDependency(bufferJob);
+
+ m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot());
+
+ const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining;
+ m_dirtyBits.marked = 0;
+ m_dirtyBits.remaining = 0;
+ BackendNodeDirtySet notCleared = 0;
+
+ // Add jobs
+ const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
+ if (entitiesEnabledDirty) {
+ renderBinJobs.push_back(m_updateTreeEnabledJob);
+ // This dependency is added here because we clear all dependencies
+ // at the start of this function.
+ m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob);
+ }
+
+ if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
+ renderBinJobs.push_back(m_worldTransformJob);
+ renderBinJobs.push_back(m_updateWorldBoundingVolumeJob);
+ renderBinJobs.push_back(m_updateShaderDataTransformJob);
+ }
+
+ if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty) {
+ renderBinJobs.push_back(m_calculateBoundingVolumeJob);
+ renderBinJobs.push_back(m_updateMeshTriangleListJob);
+ }
+
+ if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty ||
+ dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
+ renderBinJobs.push_back(m_expandBoundingVolumeJob);
+ }
+
+ m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints());
+ renderBinJobs.push_back(m_updateSkinningPaletteJob);
+ renderBinJobs.push_back(m_updateLevelOfDetailJob);
+ renderBinJobs.push_back(m_cleanupJob);
+ renderBinJobs.push_back(m_sendRenderCaptureJob);
+ renderBinJobs.push_back(m_sendBufferCaptureJob);
+ renderBinJobs.append(bufferJobs);
+
+ // Jobs to prepare GL Resource upload
+ renderBinJobs.push_back(m_vaoGathererJob);
+
+ if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty)
+ renderBinJobs.push_back(m_bufferGathererJob);
+
+ if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty) {
+ renderBinJobs.push_back(m_syncTextureLoadingJob);
+ renderBinJobs.push_back(m_textureGathererJob);
+ }
+
+
+ // Layer cache is dependent on layers, layer filters and the enabled flag
+ // on entities
+ const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty;
+ const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty;
+ const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty;
+
+ QMutexLocker lock(m_renderQueue->mutex());
+ if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case)
+ // Traverse the current framegraph. For each leaf node create a
+ // RenderView and set its configuration then create a job to
+ // populate the RenderView with a set of RenderCommands that get
+ // their details from the RenderNodes that are visible to the
+ // Camera selected by the framegraph configuration
+ FrameGraphVisitor visitor(m_nodesManager->frameGraphManager());
+ const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot());
+
+ // Remove leaf nodes that no longer exist from cache
+ const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys();
+ for (FrameGraphNode *leafNode : keys) {
+ if (!fgLeaves.contains(leafNode))
+ m_cache.leafNodeCache.remove(leafNode);
+ }
+
+ const int fgBranchCount = fgLeaves.size();
+ for (int i = 0; i < fgBranchCount; ++i) {
+ RenderViewBuilder builder(fgLeaves.at(i), i, this);
+ builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt);
+ builder.setMaterialGathererCacheNeedsToBeRebuilt(materialDirty);
+ builder.prepareJobs();
+ renderBinJobs.append(builder.buildJobHierachy());
+ }
+
+ // Set target number of RenderViews
+ m_renderQueue->setTargetRenderViewCount(fgBranchCount);
+ } else {
+ // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later
+ // if none of those jobs are started this frame
+ notCleared |= AbstractRenderer::EntityEnabledDirty;
+ notCleared |= AbstractRenderer::LayersDirty;
+ }
+
+ if (isRunning() && m_submissionContext->isInitialized()) {
+ if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty )
+ renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
+ if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty)
+ renderBinJobs.push_back(m_introspectShaderJob);
+ } else {
+ notCleared |= AbstractRenderer::TechniquesDirty;
+ notCleared |= AbstractRenderer::ShadersDirty;
+ }
+
+ m_dirtyBits.remaining = dirtyBitsForFrame & notCleared;
+
+ return renderBinJobs;
+}
+
+QAspectJobPtr Renderer::pickBoundingVolumeJob()
+{
+ // Set values on pickBoundingVolumeJob
+ RenderSettings *renderSetting = settings();
+ if (renderSetting != nullptr) {
+ m_pickBoundingVolumeJob->setRenderSettings(renderSetting);
+ m_pickBoundingVolumeJob->setFrameGraphRoot(frameGraphRoot());
+ m_pickBoundingVolumeJob->setMouseEvents(pendingPickingEvents());
+ m_pickBoundingVolumeJob->setKeyEvents(pendingKeyEvents());
+ }
+
+ return m_pickBoundingVolumeJob;
+}
+
+QAspectJobPtr Renderer::rayCastingJob()
+{
+ // Set values on rayCastingJob
+ RenderSettings *renderSetting = settings();
+ if (renderSetting != nullptr) {
+ m_rayCastingJob->setRenderSettings(renderSetting);
+ m_rayCastingJob->setFrameGraphRoot(frameGraphRoot());
+ }
+
+ return m_rayCastingJob;
+}
+
+QAspectJobPtr Renderer::syncTextureLoadingJob()
+{
+ return m_syncTextureLoadingJob;
+}
+
+QAspectJobPtr Renderer::expandBoundingVolumeJob()
+{
+ return m_expandBoundingVolumeJob;
+}
+
+QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
+{
+ return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data());
+}
+
+// Called by executeCommands
+void Renderer::performDraw(RenderCommand *command)
+{
+ // Indirect Draw Calls
+ if (command->m_drawIndirect) {
+
+ // Bind the indirect draw buffer
+ Buffer *indirectDrawBuffer = m_nodesManager->bufferManager()->data(command->m_indirectDrawBuffer);
+ if (Q_UNLIKELY(indirectDrawBuffer == nullptr)) {
+ qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve Buffer";
+ return;
+ }
+
+ // Get GLBuffer from Buffer;
+ GLBuffer *indirectDrawGLBuffer = m_submissionContext->glBufferForRenderBuffer(indirectDrawBuffer, GLBuffer::DrawIndirectBuffer);
+ if (Q_UNLIKELY(indirectDrawGLBuffer == nullptr)) {
+ qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve GLBuffer";
+ return;
+ }
+
+ // Bind GLBuffer
+ const bool successfullyBound = indirectDrawGLBuffer->bind(m_submissionContext.data(), GLBuffer::DrawIndirectBuffer);
+
+ if (Q_LIKELY(successfullyBound)) {
+ // TO DO: Handle multi draw variants if attribute count > 1
+ if (command->m_drawIndexed) {
+ m_submissionContext->drawElementsIndirect(command->m_primitiveType,
+ command->m_indexAttributeDataType,
+ reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset)));
+ } else {
+ m_submissionContext->drawArraysIndirect(command->m_primitiveType,
+ reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset)));
+ }
+ } else {
+ qWarning() << "Failed to bind IndirectDrawBuffer";
+ }
+
+ } else { // Direct Draw Calls
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_primitiveType == QGeometryRenderer::Patches)
+ m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch);
+
+ if (command->m_primitiveRestartEnabled)
+ m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue);
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_drawIndexed) {
+ Profiling::GLTimeRecorder recorder(Profiling::DrawElement);
+ m_submissionContext->drawElementsInstancedBaseVertexBaseInstance(command->m_primitiveType,
+ command->m_primitiveCount,
+ command->m_indexAttributeDataType,
+ reinterpret_cast<void*>(quintptr(command->m_indexAttributeByteOffset)),
+ command->m_instanceCount,
+ command->m_indexOffset,
+ command->m_firstVertex);
+ } else {
+ Profiling::GLTimeRecorder recorder(Profiling::DrawArray);
+ m_submissionContext->drawArraysInstancedBaseInstance(command->m_primitiveType,
+ command->m_firstInstance,
+ command->m_primitiveCount,
+ command->m_instanceCount,
+ command->m_firstVertex);
+ }
+ }
+
+#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
+ int err = m_graphicsContext->openGLContext()->functions()->glGetError();
+ if (err)
+ qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
+#endif
+
+ if (command->m_primitiveRestartEnabled)
+ m_submissionContext->disablePrimitiveRestart();
+}
+
+void Renderer::performCompute(const RenderView *, RenderCommand *command)
+{
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate);
+ m_submissionContext->activateShader(command->m_shaderDna);
+ }
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate);
+ m_submissionContext->setParameters(command->m_parameterPack);
+ }
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute);
+ m_submissionContext->dispatchCompute(command->m_workGroups[0],
+ command->m_workGroups[1],
+ command->m_workGroups[2]);
+ }
+ // HACK: Reset the compute flag to dirty
+ m_dirtyBits.marked |= AbstractRenderer::ComputeDirty;
+
+#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
+ int err = m_graphicsContext->openGLContext()->functions()->glGetError();
+ if (err)
+ qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
+#endif
+}
+
+void Renderer::createOrUpdateVAO(RenderCommand *command,
+ HVao *previousVaoHandle,
+ OpenGLVertexArrayObject **vao)
+{
+ const VAOIdentifier vaoKey(command->m_geometry, command->m_shader);
+
+ VAOManager *vaoManager = m_nodesManager->vaoManager();
+ command->m_vao = vaoManager->lookupHandle(vaoKey);
+
+ if (command->m_vao.isNull()) {
+ qCDebug(Rendering) << Q_FUNC_INFO << "Allocating new VAO";
+ command->m_vao = vaoManager->getOrAcquireHandle(vaoKey);
+ vaoManager->data(command->m_vao)->create(m_submissionContext.data(), vaoKey);
+ }
+
+ if (*previousVaoHandle != command->m_vao) {
+ *previousVaoHandle = command->m_vao;
+ *vao = vaoManager->data(command->m_vao);
+ }
+ Q_ASSERT(*vao);
+}
+
+// Called by RenderView->submit() in RenderThread context
+// Returns true, if all RenderCommands were sent to the GPU
+bool Renderer::executeCommandsSubmission(const RenderView *rv)
+{
+ bool allCommandsIssued = true;
+
+ // Render drawing commands
+ const QVector<RenderCommand *> commands = rv->commands();
+
+ // Use the graphicscontext to submit the commands to the underlying
+ // graphics API (OpenGL)
+
+ // Save the RenderView base stateset
+ RenderStateSet *globalState = m_submissionContext->currentStateSet();
+ OpenGLVertexArrayObject *vao = nullptr;
+
+ for (RenderCommand *command : qAsConst(commands)) {
+
+ if (command->m_type == RenderCommand::Compute) { // Compute Call
+ performCompute(rv, command);
+ } else { // Draw Command
+ // Check if we have a valid command that can be drawn
+ if (!command->m_isValid) {
+ allCommandsIssued = false;
+ continue;
+ }
+
+ vao = m_nodesManager->vaoManager()->data(command->m_vao);
+
+ // something may have went wrong when initializing the VAO
+ if (!vao->isSpecified()) {
+ allCommandsIssued = false;
+ continue;
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate);
+ //// We activate the shader here
+ if (!m_submissionContext->activateShader(command->m_shaderDna)) {
+ allCommandsIssued = false;
+ continue;
+ }
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::VAOUpdate);
+ // Bind VAO
+ vao->bind();
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate);
+ //// Update program uniforms
+ if (!m_submissionContext->setParameters(command->m_parameterPack)) {
+ allCommandsIssued = false;
+ // If we have failed to set uniform (e.g unable to bind a texture)
+ // we won't perform the draw call which could show invalid content
+ continue;
+ }
+ }
+
+ //// OpenGL State
+ // TO DO: Make states not dependendent on their backend node for this step
+ // Set state
+ RenderStateSet *localState = command->m_stateSet;
+
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::StateUpdate);
+ // Merge the RenderCommand state with the globalState of the RenderView
+ // Or restore the globalState if no stateSet for the RenderCommand
+ if (localState != nullptr) {
+ command->m_stateSet->merge(globalState);
+ m_submissionContext->setCurrentStateSet(command->m_stateSet);
+ } else {
+ m_submissionContext->setCurrentStateSet(globalState);
+ }
+ }
+ // All Uniforms for a pass are stored in the QUniformPack of the command
+ // Uniforms for Effect, Material and Technique should already have been correctly resolved
+ // at that point
+
+ //// Draw Calls
+ performDraw(command);
+ }
+ } // end of RenderCommands loop
+
+ // We cache the VAO and release it only at the end of the exectute frame
+ // We try to minimize VAO binding between RenderCommands
+ if (vao)
+ vao->release();
+
+ // Reset to the state we were in before executing the render commands
+ m_submissionContext->setCurrentStateSet(globalState);
+
+ return allCommandsIssued;
+}
+
+bool Renderer::updateVAOWithAttributes(Geometry *geometry,
+ RenderCommand *command,
+ Shader *shader,
+ bool forceUpdate)
+{
+ m_dirtyAttributes.reserve(m_dirtyAttributes.size() + geometry->attributes().size());
+ const auto attributeIds = geometry->attributes();
+
+ for (QNodeId attributeId : attributeIds) {
+ // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager
+ Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId);
+
+ if (attribute == nullptr)
+ return false;
+
+ Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attribute->bufferId());
+
+ // Buffer update was already performed at this point
+ // Just make sure the attribute reference a valid buffer
+ if (buffer == nullptr)
+ return false;
+
+ // Index Attribute
+ bool attributeWasDirty = false;
+ if (attribute->attributeType() == QAttribute::IndexAttribute) {
+ if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate)
+ m_submissionContext->specifyIndices(buffer);
+ // Vertex Attribute
+ } else if (command->m_attributes.contains(attribute->nameId())) {
+ if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) {
+ // Find the location for the attribute
+ const QVector<ShaderAttribute> shaderAttributes = shader->attributes();
+ const ShaderAttribute *attributeDescription = nullptr;
+ for (const ShaderAttribute &shaderAttribute : shaderAttributes) {
+ if (shaderAttribute.m_nameId == attribute->nameId()) {
+ attributeDescription = &shaderAttribute;
+ break;
+ }
+ }
+ if (!attributeDescription || attributeDescription->m_location < 0)
+ return false;
+ m_submissionContext->specifyAttribute(attribute, buffer, attributeDescription);
+ }
+ }
+
+ // Append attribute to temporary vector so that its dirtiness
+ // can be cleared at the end of the frame
+ if (attributeWasDirty)
+ m_dirtyAttributes.push_back(attribute);
+
+ // Note: We cannot call unsertDirty on the Attribute at this
+ // point as we don't know if the attributes are being shared
+ // with other geometry / geometryRenderer in which case they still
+ // should remain dirty so that VAO for these commands are properly
+ // updated
+ }
+
+ return true;
+}
+
+bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry,
+ RenderCommand *command) const
+{
+ const auto attributeIds = geometry->attributes();
+
+ for (QNodeId attributeId : attributeIds) {
+ // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager
+ Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId);
+
+ if (attribute == nullptr)
+ continue;
+
+ if ((attribute->attributeType() == QAttribute::IndexAttribute && attribute->isDirty()) ||
+ (command->m_attributes.contains(attribute->nameId()) && attribute->isDirty()))
+ return true;
+ }
+ return false;
+}
+
+// Erase graphics related resources that may become unused after a frame
+void Renderer::cleanGraphicsResources()
+{
+ // Clean buffers
+ const QVector<Qt3DCore::QNodeId> buffersToRelease = m_nodesManager->bufferManager()->takeBuffersToRelease();
+ for (Qt3DCore::QNodeId bufferId : buffersToRelease)
+ m_submissionContext->releaseBuffer(bufferId);
+
+ // Delete abandoned textures
+ const QVector<GLTexture*> abandonedTextures = m_nodesManager->glTextureManager()->takeAbandonedTextures();
+ for (GLTexture *tex : abandonedTextures) {
+ tex->destroyGLTexture();
+ delete tex;
+ }
+
+ // Delete abandoned VAOs
+ m_abandonedVaosMutex.lock();
+ const QVector<HVao> abandonedVaos = std::move(m_abandonedVaos);
+ m_abandonedVaosMutex.unlock();
+ for (const HVao &vaoHandle : abandonedVaos) {
+ // might have already been destroyed last frame, but added by the cleanup job before, so
+ // check if the VAO is really still existent
+ OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle);
+ if (vao) {
+ vao->destroy();
+ m_nodesManager->vaoManager()->release(vaoHandle);
+ }
+ }
+}
+
+QList<QPair<QObject *, QMouseEvent>> Renderer::pendingPickingEvents() const
+{
+ return m_pickEventFilter->pendingMouseEvents();
+}
+
+QList<QKeyEvent> Renderer::pendingKeyEvents() const
+{
+ return m_pickEventFilter->pendingKeyEvents();
+}
+
+const GraphicsApiFilterData *Renderer::contextInfo() const
+{
+ return m_submissionContext->contextInfo();
+}
+
+SubmissionContext *Renderer::submissionContext() const
+{
+ return m_submissionContext.data();
+}
+
+void Renderer::addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId)
+{
+ if (!m_pendingRenderCaptureSendRequests.contains(nodeId))
+ m_pendingRenderCaptureSendRequests.push_back(nodeId);
+}
+
+const QVector<Qt3DCore::QNodeId> Renderer::takePendingRenderCaptureSendRequests()
+{
+ return std::move(m_pendingRenderCaptureSendRequests);
+}
+
+// Returns a vector of jobs to be performed for dirty buffers
+// 1 dirty buffer == 1 job, all job can be performed in parallel
+QVector<Qt3DCore::QAspectJobPtr> Renderer::createRenderBufferJobs() const
+{
+ const QVector<QNodeId> dirtyBuffers = m_nodesManager->bufferManager()->takeDirtyBuffers();
+ QVector<QAspectJobPtr> dirtyBuffersJobs;
+ dirtyBuffersJobs.reserve(dirtyBuffers.size());
+
+ for (const QNodeId bufId : dirtyBuffers) {
+ Render::HBuffer bufferHandle = m_nodesManager->lookupHandle<Render::Buffer, Render::BufferManager, Render::HBuffer>(bufId);
+ if (!bufferHandle.isNull()) {
+ // Create new buffer job
+ auto job = Render::LoadBufferJobPtr::create(bufferHandle);
+ job->setNodeManager(m_nodesManager);
+ dirtyBuffersJobs.push_back(job);
+ }
+ }
+
+ return dirtyBuffersJobs;
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/renderer.pri b/src/render/renderers/opengl/renderer/renderer.pri
new file mode 100644
index 000000000..34f6064bd
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderer.pri
@@ -0,0 +1,27 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/commandthread.cpp \
+ $$PWD/glcommands.cpp \
+ $$PWD/openglvertexarrayobject.cpp \
+ $$PWD/rendercommand.cpp \
+ $$PWD/renderer.cpp \
+ $$PWD/renderqueue.cpp \
+ $$PWD/renderview.cpp \
+ $$PWD/renderviewbuilder.cpp \
+ $$PWD/shaderparameterpack.cpp
+
+HEADERS += \
+ $$PWD/commandthread_p.h \
+ $$PWD/glcommands_p.h \
+ $$PWD/openglvertexarrayobject_p.h \
+ $$PWD/renderercache_p.h \
+ $$PWD/rendercommand_p.h \
+ $$PWD/renderer_p.h \
+ $$PWD/renderqueue_p.h \
+ $$PWD/renderview_p.h \
+ $$PWD/renderviewbuilder_p.h \
+ $$PWD/shaderparameterpack_p.h \
+ $$PWD/shadervariables_p.h
+
+
diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h
new file mode 100644
index 000000000..b4ad0b0fe
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderer_p.h
@@ -0,0 +1,405 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERER_H
+#define QT3DRENDER_RENDER_RENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/private/shaderparameterpack_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/abstractrenderer_p.h>
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <Qt3DRender/private/pickboundingvolumejob_p.h>
+#include <Qt3DRender/private/raycastingjob_p.h>
+#include <Qt3DRender/private/rendersettings_p.h>
+#include <Qt3DRender/private/renderviewinitializerjob_p.h>
+#include <Qt3DRender/private/expandboundingvolumejob_p.h>
+#include <Qt3DRender/private/updateworldtransformjob_p.h>
+#include <Qt3DRender/private/calcboundingvolumejob_p.h>
+#include <Qt3DRender/private/updateshaderdatatransformjob_p.h>
+#include <Qt3DRender/private/framecleanupjob_p.h>
+#include <Qt3DRender/private/updateworldboundingvolumejob_p.h>
+#include <Qt3DRender/private/updatetreeenabledjob_p.h>
+#include <Qt3DRender/private/platformsurfacefilter_p.h>
+#include <Qt3DRender/private/sendrendercapturejob_p.h>
+#include <Qt3DRender/private/sendbuffercapturejob_p.h>
+#include <Qt3DRender/private/genericlambdajob_p.h>
+#include <Qt3DRender/private/updatemeshtrianglelistjob_p.h>
+#include <Qt3DRender/private/filtercompatibletechniquejob_p.h>
+#include <Qt3DRender/private/updateskinningpalettejob_p.h>
+#include <Qt3DRender/private/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>
+
+QT_BEGIN_NAMESPACE
+
+class QSurface;
+class QMouseEvent;
+
+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;
+
+#if QT_CONFIG(qt3d_profile_jobs)
+namespace Debug {
+class CommandExecuter;
+}
+#endif
+
+namespace Render {
+
+class CameraLens;
+class SubmissionContext;
+class FrameGraphNode;
+class Material;
+class Technique;
+class Shader;
+class Entity;
+class RenderCommand;
+class RenderQueue;
+class RenderView;
+class Effect;
+class RenderPass;
+class RenderThread;
+class CommandThread;
+class RenderStateSet;
+class VSyncFrameAdvanceService;
+class PickEventFilter;
+class NodeManagers;
+class ShaderCache;
+
+class UpdateLevelOfDetailJob;
+typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr;
+
+using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+using IntrospectShadersJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+
+class QT3DRENDERSHARED_PRIVATE_EXPORT Renderer : public AbstractRenderer
+{
+public:
+ explicit Renderer(QRenderAspect::RenderType type);
+ ~Renderer();
+
+ void dumpInfo() const override;
+ API api() const override { return AbstractRenderer::OpenGL; }
+
+ qint64 time() const override;
+ void setTime(qint64 time) override;
+
+ void setNodeManagers(NodeManagers *managers) override;
+ void setServices(Qt3DCore::QServiceLocator *services) override;
+ void setSurfaceExposed(bool exposed) override;
+
+ NodeManagers *nodeManagers() const override;
+ Qt3DCore::QServiceLocator *services() const override { return m_services; }
+
+ void initialize() override;
+ void shutdown() override;
+ void releaseGraphicsResources() override;
+
+ void render() override;
+ void doRender(bool scene3dBlocking = false) override;
+ void cleanGraphicsResources() override;
+
+ bool isRunning() const override { return m_running.load(); }
+
+ void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Entity *sgRoot) override;
+ Entity *sceneRoot() const override { return m_renderSceneRoot; }
+
+ FrameGraphNode *frameGraphRoot() const override;
+
+ void markDirty(BackendNodeDirtySet changes, BackendNode *node) override;
+ BackendNodeDirtySet dirtyBits() override;
+
+#if defined(QT_BUILD_INTERNAL)
+ void clearDirtyBits(BackendNodeDirtySet changes) override;
+#endif
+ bool shouldRender() override;
+ void skipNextFrame() override;
+
+ QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override;
+ Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override;
+ Qt3DCore::QAspectJobPtr rayCastingJob() override;
+ Qt3DCore::QAspectJobPtr syncTextureLoadingJob() override;
+ Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() override;
+
+ QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const;
+
+ inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; }
+ inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; }
+ inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; }
+ inline UpdateTreeEnabledJobPtr updateTreeEnabledJob() const { return m_updateTreeEnabledJob; }
+ inline UpdateWorldTransformJobPtr updateWorldTransformJob() const { return m_worldTransformJob; }
+ inline UpdateWorldBoundingVolumeJobPtr updateWorldBoundingVolumeJob() const { return m_updateWorldBoundingVolumeJob; }
+ inline UpdateLevelOfDetailJobPtr updateLevelOfDetailJob() const { return m_updateLevelOfDetailJob; }
+ inline UpdateMeshTriangleListJobPtr updateMeshTriangleListJob() const { return m_updateMeshTriangleListJob; }
+ inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const { return m_filterCompatibleTechniqueJob; }
+ inline SynchronizerJobPtr textureLoadSyncJob() const { return m_syncTextureLoadingJob; }
+ inline UpdateSkinningPaletteJobPtr updateSkinningPaletteJob() const { return m_updateSkinningPaletteJob; }
+ inline IntrospectShadersJobPtr introspectShadersJob() const { return m_introspectShaderJob; }
+ inline Qt3DCore::QAspectJobPtr bufferGathererJob() const { return m_bufferGathererJob; }
+ inline Qt3DCore::QAspectJobPtr textureGathererJob() const { return m_textureGathererJob; }
+
+ Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override;
+
+ void registerEventFilter(Qt3DCore::QEventFilterService *service) override;
+
+ void setSettings(RenderSettings *settings) override;
+ RenderSettings *settings() const override;
+ QOpenGLContext *shareContext() const override;
+
+
+ // Executed in secondary GL thread
+ void loadShader(Shader *shader) const override;
+
+
+ void updateGLResources();
+ void updateTexture(Texture *texture);
+ void cleanupTexture(const Texture *texture);
+ void downloadGLBuffers();
+ void blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
+ Qt3DCore::QNodeId outputRenderTargetId,
+ QRect inputRect,
+ QRect outputRect,
+ GLuint defaultFramebuffer);
+
+ void prepareCommandsSubmission(const QVector<RenderView *> &renderViews);
+ bool executeCommandsSubmission(const RenderView *rv);
+ bool updateVAOWithAttributes(Geometry *geometry,
+ RenderCommand *command,
+ Shader *shader,
+ bool forceUpdate);
+
+ bool requiresVAOAttributeUpdate(Geometry *geometry,
+ RenderCommand *command) const;
+
+ void setOpenGLContext(QOpenGLContext *context);
+ 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 addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId);
+ const QVector<Qt3DCore::QNodeId> takePendingRenderCaptureSendRequests();
+
+ void enqueueRenderView(RenderView *renderView, int submitOrder);
+ bool isReadyToSubmit();
+
+ QVariant executeCommand(const QStringList &args) override;
+ void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override;
+ QSurfaceFormat format() override;
+
+ struct ViewSubmissionResultData
+ {
+ ViewSubmissionResultData()
+ : lastBoundFBOId(0)
+ , surface(nullptr)
+ {}
+
+ uint lastBoundFBOId;
+ QSurface *surface;
+ };
+
+ ViewSubmissionResultData submitRenderViews(const QVector<Render::RenderView *> &renderViews);
+
+ RendererCache *cache() { return &m_cache; }
+
+#ifdef QT3D_RENDER_UNIT_TESTS
+public:
+#else
+
+private:
+#endif
+ bool canRender() const;
+
+ Qt3DCore::QServiceLocator *m_services;
+ NodeManagers *m_nodesManager;
+
+ // Frame graph root
+ Qt3DCore::QNodeId m_frameGraphRootUuid;
+
+ Entity *m_renderSceneRoot;
+
+ // Fail safe values that we can use if a RenderCommand
+ // is missing a shader
+ RenderStateSet *m_defaultRenderStateSet;
+ ShaderParameterPack m_defaultUniformPack;
+
+ QScopedPointer<SubmissionContext> m_submissionContext;
+ QSurfaceFormat m_format;
+
+ RenderQueue *m_renderQueue;
+ QScopedPointer<RenderThread> m_renderThread;
+ QScopedPointer<CommandThread> m_commandThread;
+ QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService;
+
+ QSemaphore m_submitRenderViewsSemaphore;
+ QSemaphore m_waitForInitializationToBeCompleted;
+
+ QAtomicInt m_running;
+
+ QScopedPointer<PickEventFilter> m_pickEventFilter;
+
+ QVector<Attribute *> m_dirtyAttributes;
+ QVector<Geometry *> m_dirtyGeometry;
+ QAtomicInt m_exposed;
+
+ struct DirtyBits {
+ BackendNodeDirtySet marked = 0; // marked dirty since last job build
+ BackendNodeDirtySet remaining = 0; // remaining dirty after jobs have finished
+ };
+ DirtyBits m_dirtyBits;
+
+ QAtomicInt m_lastFrameCorrect;
+ QOpenGLContext *m_glContext;
+ QOpenGLContext *m_shareContext;
+ mutable QMutex m_shareContextMutex;
+ ShaderCache *m_shaderCache;
+ PickBoundingVolumeJobPtr m_pickBoundingVolumeJob;
+ RayCastingJobPtr m_rayCastingJob;
+
+ qint64 m_time;
+
+ RenderSettings *m_settings;
+
+ UpdateShaderDataTransformJobPtr m_updateShaderDataTransformJob;
+ FrameCleanupJobPtr m_cleanupJob;
+ UpdateWorldTransformJobPtr m_worldTransformJob;
+ ExpandBoundingVolumeJobPtr m_expandBoundingVolumeJob;
+ CalculateBoundingVolumeJobPtr m_calculateBoundingVolumeJob;
+ UpdateWorldBoundingVolumeJobPtr m_updateWorldBoundingVolumeJob;
+ UpdateTreeEnabledJobPtr m_updateTreeEnabledJob;
+ SendRenderCaptureJobPtr m_sendRenderCaptureJob;
+ SendBufferCaptureJobPtr m_sendBufferCaptureJob;
+ UpdateSkinningPaletteJobPtr m_updateSkinningPaletteJob;
+ UpdateLevelOfDetailJobPtr m_updateLevelOfDetailJob;
+ UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob;
+ FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob;
+
+ QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests;
+
+ void performDraw(RenderCommand *command);
+ void performCompute(const RenderView *rv, RenderCommand *command);
+ void createOrUpdateVAO(RenderCommand *command,
+ HVao *previousVAOHandle,
+ OpenGLVertexArrayObject **vao);
+
+ GenericLambdaJobPtr<std::function<void ()>> m_bufferGathererJob;
+ GenericLambdaJobPtr<std::function<void ()>> m_vaoGathererJob;
+ GenericLambdaJobPtr<std::function<void ()>> m_textureGathererJob;
+ IntrospectShadersJobPtr m_introspectShaderJob;
+
+ SynchronizerJobPtr m_syncTextureLoadingJob;
+
+ void lookForAbandonedVaos();
+ void lookForDirtyBuffers();
+ void lookForDownloadableBuffers();
+ void lookForDirtyTextures();
+ void reloadDirtyShaders();
+
+ QMutex m_abandonedVaosMutex;
+ QVector<HVao> m_abandonedVaos;
+
+ QVector<HBuffer> m_dirtyBuffers;
+ QVector<HBuffer> m_downloadableBuffers;
+ QVector<HTexture> m_dirtyTextures;
+
+ bool m_ownedContext;
+
+ OffscreenSurfaceHelper *m_offscreenHelper;
+ QMutex m_offscreenSurfaceMutex;
+
+#if QT_CONFIG(qt3d_profile_jobs)
+ QScopedPointer<Qt3DRender::Debug::CommandExecuter> m_commandExecuter;
+ friend class Qt3DRender::Debug::CommandExecuter;
+#endif
+
+ QMetaObject::Connection m_contextConnection;
+ RendererCache m_cache;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERER_H
diff --git a/src/render/renderers/opengl/renderer/renderercache_p.h b/src/render/renderers/opengl/renderer/renderercache_p.h
new file mode 100644
index 000000000..2aa50d131
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderercache_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERERCACHE_P_H
+#define QT3DRENDER_RENDER_RENDERERCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/QFrameGraphNode>
+
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/renderviewjobutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+struct RendererCache
+{
+ struct LeafNodeData
+ {
+ QVector<Entity *> filterEntitiesByLayer;
+ MaterialParameterGathererData materialParameterGatherer;
+ };
+
+ QHash<FrameGraphNode *, LeafNodeData> leafNodeCache;
+
+ QMutex *mutex() { return &m_mutex; }
+
+private:
+ QMutex m_mutex;
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERERCACHE_P_H
diff --git a/src/render/renderers/opengl/renderer/renderqueue.cpp b/src/render/renderers/opengl/renderer/renderqueue.cpp
new file mode 100644
index 000000000..bd9d3ee59
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderqueue.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderqueue_p.h"
+#include <Qt3DRender/private/renderview_p.h>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+RenderQueue::RenderQueue()
+ : m_noRender(false)
+ , m_wasReset(true)
+ , m_targetRenderViewCount(0)
+ , m_currentRenderViewCount(0)
+ , m_currentWorkQueue(1)
+{
+}
+
+int RenderQueue::currentRenderViewCount() const
+{
+ return m_currentRenderViewCount;
+}
+
+/*
+ * In case the framegraph changed or when the current number of render queue
+ * needs to be reset.
+ */
+void RenderQueue::reset()
+{
+ m_currentRenderViewCount = 0;
+ m_targetRenderViewCount = 0;
+ m_currentWorkQueue.clear();
+ m_noRender = false;
+ m_wasReset = true;
+}
+
+void RenderQueue::setNoRender()
+{
+ Q_ASSERT(m_targetRenderViewCount == 0);
+ m_noRender = true;
+}
+
+/*
+ * Queue up a RenderView for the frame being built.
+ * Thread safe as this is called from the renderer which is locked.
+ * Returns true if the renderView is complete
+ */
+bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIndex)
+{
+ Q_ASSERT(!m_noRender);
+ m_currentWorkQueue[submissionOrderIndex] = renderView;
+ ++m_currentRenderViewCount;
+ Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount);
+ return isFrameQueueComplete();
+}
+
+/*
+ * Called by the Rendering Thread to retrieve the a frame queue to render.
+ * A call to reset is required after rendering of the frame. Otherwise under some
+ * conditions the current but then invalidated frame queue could be reused.
+ */
+QVector<RenderView *> RenderQueue::nextFrameQueue()
+{
+ return m_currentWorkQueue;
+}
+
+/*
+ * Sets the number \a targetRenderViewCount of RenderView objects that make up a frame.
+ */
+void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount)
+{
+ Q_ASSERT(!m_noRender);
+ m_targetRenderViewCount = targetRenderViewCount;
+ m_currentWorkQueue.resize(targetRenderViewCount);
+ m_wasReset = false;
+}
+
+/*
+ * Returns true if all the RenderView objects making up the current frame have been queued.
+ * Returns false otherwise.
+ * \note a frameQueue or size 0 is considered incomplete.
+ */
+bool RenderQueue::isFrameQueueComplete() const
+{
+ return (m_noRender
+ || (m_targetRenderViewCount > 0 && m_targetRenderViewCount == m_currentRenderViewCount));
+}
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/renderqueue_p.h b/src/render/renderers/opengl/renderer/renderqueue_p.h
new file mode 100644
index 000000000..e565115f2
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderqueue_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERQUEUE_H
+#define QT3DRENDER_RENDER_RENDERQUEUE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QVector>
+#include <QtGlobal>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class RenderView;
+
+class Q_AUTOTEST_EXPORT RenderQueue
+{
+public:
+ RenderQueue();
+
+ void setTargetRenderViewCount(int targetRenderViewCount);
+ int targetRenderViewCount() const { return m_targetRenderViewCount; }
+ int currentRenderViewCount() const;
+ bool isFrameQueueComplete() const;
+
+ bool queueRenderView(RenderView *renderView, uint submissionOrderIndex);
+ QVector<RenderView *> nextFrameQueue();
+ void reset();
+
+ void setNoRender();
+ inline bool isNoRender() const { return m_noRender; }
+
+ inline bool wasReset() const { return m_wasReset; }
+
+ inline QMutex *mutex() { return &m_mutex; }
+
+private:
+ bool m_noRender;
+ bool m_wasReset;
+ int m_targetRenderViewCount;
+ int m_currentRenderViewCount;
+ QVector<RenderView *> m_currentWorkQueue;
+ QMutex m_mutex;
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERQUEUE_H
diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp
new file mode 100644
index 000000000..c29448570
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderview.cpp
@@ -0,0 +1,1108 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderview_p.h"
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qrendertarget.h>
+#include <Qt3DRender/qabstractlight.h>
+#include <Qt3DRender/private/sphere_p.h>
+
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/framegraphnode_p.h>
+#include <Qt3DRender/private/layerfilternode_p.h>
+#include <Qt3DRender/private/qparameter_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/rendercommand_p.h>
+#include <Qt3DRender/private/effect_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/renderer_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/renderstateset_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 <Qt3DCore/qentity.h>
+#include <QtGui/qsurface.h>
+#include <algorithm>
+
+#include <QDebug>
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+
+namespace {
+
+// register our QNodeId's as a metatype during program loading
+const int Q_DECL_UNUSED qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
+
+const int MAX_LIGHTS = 8;
+
+#define LIGHT_POSITION_NAME QLatin1String(".position")
+#define LIGHT_TYPE_NAME QLatin1String(".type")
+#define LIGHT_COLOR_NAME QLatin1String(".color")
+#define LIGHT_INTENSITY_NAME QLatin1String(".intensity")
+
+int LIGHT_COUNT_NAME_ID = 0;
+int LIGHT_POSITION_NAMES[MAX_LIGHTS];
+int LIGHT_TYPE_NAMES[MAX_LIGHTS];
+int LIGHT_COLOR_NAMES[MAX_LIGHTS];
+int LIGHT_INTENSITY_NAMES[MAX_LIGHTS];
+QString LIGHT_STRUCT_NAMES[MAX_LIGHTS];
+
+} // anonymous namespace
+
+bool wasInitialized = false;
+RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters;
+
+
+RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniformSetters()
+{
+ RenderView::StandardUniformsNameToTypeHash setters;
+
+ setters.insert(StringToInt::lookupId(QLatin1String("modelMatrix")), ModelMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("viewMatrix")), ViewMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("projectionMatrix")), ProjectionMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("modelView")), ModelViewMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("viewProjectionMatrix")), ViewProjectionMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("modelViewProjection")), ModelViewProjectionMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("mvp")), ModelViewProjectionMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("inverseModelMatrix")), InverseModelMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("inverseViewMatrix")), InverseViewMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("inverseProjectionMatrix")), InverseProjectionMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("inverseModelView")), InverseModelViewMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("inverseViewProjectionMatrix")), InverseViewProjectionMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("inverseModelViewProjection")), InverseModelViewProjectionMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("modelNormalMatrix")), ModelNormalMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("modelViewNormal")), ModelViewNormalMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("viewportMatrix")), ViewportMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("inverseViewportMatrix")), InverseViewportMatrix);
+ setters.insert(StringToInt::lookupId(QLatin1String("aspectRatio")), AspectRatio);
+ setters.insert(StringToInt::lookupId(QLatin1String("exposure")), Exposure);
+ setters.insert(StringToInt::lookupId(QLatin1String("gamma")), Gamma);
+ setters.insert(StringToInt::lookupId(QLatin1String("time")), Time);
+ setters.insert(StringToInt::lookupId(QLatin1String("eyePosition")), EyePosition);
+ setters.insert(StringToInt::lookupId(QLatin1String("skinningPalette[0]")), SkinningPalette);
+
+ return setters;
+}
+
+// TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too
+static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize)
+{
+ return QRectF(fractionalViewport.x() * surfaceSize.width(),
+ (1.0 - fractionalViewport.y() - fractionalViewport.height()) * surfaceSize.height(),
+ fractionalViewport.width() * surfaceSize.width(),
+ fractionalViewport.height() * surfaceSize.height());
+}
+
+static Matrix4x4 getProjectionMatrix(const CameraLens *lens)
+{
+ if (!lens)
+ qWarning() << "[Qt3D Renderer] No Camera Lens found. Add a CameraSelector to your Frame Graph or make sure that no entities will be rendered.";
+ return lens ? lens->projection() : Matrix4x4();
+}
+
+UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType,
+ Entity *entity,
+ const Matrix4x4 &model) const
+{
+ switch (standardUniformType) {
+ case ModelMatrix:
+ return UniformValue(model);
+ case ViewMatrix:
+ return UniformValue(m_data.m_viewMatrix);
+ case ProjectionMatrix:
+ return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens));
+ case ModelViewMatrix:
+ return UniformValue(m_data.m_viewMatrix * model);
+ case ViewProjectionMatrix:
+ return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix);
+ case ModelViewProjectionMatrix:
+ return UniformValue(m_data.m_viewProjectionMatrix * model);
+ case InverseModelMatrix:
+ return UniformValue(model.inverted());
+ case InverseViewMatrix:
+ return UniformValue(m_data.m_viewMatrix.inverted());
+ case InverseProjectionMatrix: {
+ return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens).inverted());
+ }
+ case InverseModelViewMatrix:
+ return UniformValue((m_data.m_viewMatrix * model).inverted());
+ case InverseViewProjectionMatrix: {
+ const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix;
+ return UniformValue(viewProjectionMatrix.inverted());
+ }
+ case InverseModelViewProjectionMatrix:
+ return UniformValue((m_data.m_viewProjectionMatrix * model).inverted());
+ case ModelNormalMatrix:
+ return UniformValue(convertToQMatrix4x4(model).normalMatrix());
+ case ModelViewNormalMatrix:
+ return UniformValue(convertToQMatrix4x4(m_data.m_viewMatrix * model).normalMatrix());
+ case ViewportMatrix: {
+ QMatrix4x4 viewportMatrix;
+ // TO DO: Implement on Matrix4x4
+ viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize));
+ return UniformValue(Matrix4x4(viewportMatrix));
+ }
+ case InverseViewportMatrix: {
+ QMatrix4x4 viewportMatrix;
+ // TO DO: Implement on Matrix4x4
+ viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize));
+ return UniformValue(Matrix4x4(viewportMatrix.inverted()));
+ }
+ case AspectRatio:
+ return float(m_surfaceSize.width()) / float(m_surfaceSize.height());
+ case Exposure:
+ return UniformValue(m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f);
+ case Gamma:
+ return UniformValue(m_gamma);
+ case Time:
+ return UniformValue(float(m_renderer->time() / 1000000000.0f));
+ case EyePosition:
+ return UniformValue(m_data.m_eyePos);
+ case SkinningPalette: {
+ const Armature *armature = entity->renderComponent<Armature>();
+ if (!armature) {
+ qCWarning(Jobs, "Requesting skinningPalette uniform but no armature set on entity");
+ return UniformValue();
+ }
+ return armature->skinningPaletteUniform();
+ }
+ default:
+ Q_UNREACHABLE();
+ return UniformValue();
+ }
+}
+
+RenderView::RenderView()
+ : m_isDownloadBuffersEnable(false)
+ , m_hasBlitFramebufferInfo(false)
+ , m_renderer(nullptr)
+ , m_devicePixelRatio(1.)
+ , m_viewport(QRectF(0.0f, 0.0f, 1.0f, 1.0f))
+ , m_gamma(2.2f)
+ , m_surface(nullptr)
+ , m_clearBuffer(QClearBuffers::None)
+ , m_stateSet(nullptr)
+ , m_noDraw(false)
+ , m_compute(false)
+ , m_frustumCulling(false)
+ , m_memoryBarrier(QMemoryBarrier::None)
+ , m_environmentLight(nullptr)
+{
+ m_workGroups[0] = 1;
+ m_workGroups[1] = 1;
+ m_workGroups[2] = 1;
+
+ if (Q_UNLIKELY(!wasInitialized)) {
+ // Needed as we can control the init order of static/global variables across compile units
+ // and this hash relies on the static StringToInt class
+ wasInitialized = true;
+ RenderView::ms_standardUniformSetters = RenderView::initializeStandardUniformSetters();
+ LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount"));
+ for (int i = 0; i < MAX_LIGHTS; ++i) {
+ Q_STATIC_ASSERT_X(MAX_LIGHTS < 10, "can't use the QChar trick anymore");
+ LIGHT_STRUCT_NAMES[i] = QLatin1String("lights[") + QLatin1Char(char('0' + i)) + QLatin1Char(']');
+ LIGHT_POSITION_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_POSITION_NAME);
+ LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME);
+ LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME);
+ LIGHT_INTENSITY_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME);
+ }
+ }
+}
+
+RenderView::~RenderView()
+{
+ delete m_stateSet;
+ for (RenderCommand *command : qAsConst(m_commands)) {
+ delete command->m_stateSet;
+ delete command;
+ }
+}
+
+namespace {
+
+template<int SortType>
+struct AdjacentSubRangeFinder
+{
+ static bool adjacentSubRange(RenderCommand *, RenderCommand *)
+ {
+ Q_UNREACHABLE();
+ return false;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>
+{
+ static bool adjacentSubRange(RenderCommand *a, RenderCommand *b)
+ {
+ return a->m_changeCost == b->m_changeCost;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::BackToFront>
+{
+ static bool adjacentSubRange(RenderCommand *a, RenderCommand *b)
+ {
+ return a->m_depth == b->m_depth;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::Material>
+{
+ static bool adjacentSubRange(RenderCommand *a, RenderCommand *b)
+ {
+ return a->m_shaderDna == b->m_shaderDna;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack>
+{
+ static bool adjacentSubRange(RenderCommand *a, RenderCommand *b)
+ {
+ return a->m_depth == b->m_depth;
+ }
+};
+
+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, [] (RenderCommand *a, 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, [] (RenderCommand *a, 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 shaderDNA
+ std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) {
+ return a->m_shaderDna > b->m_shaderDna;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::FrontToBack>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) {
+ return a->m_depth < b->m_depth;
+ });
+ }
+};
+
+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);
+ 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, [] (RenderCommand *a, 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;
+ 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()
+{
+ 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;
+ while (i < m_commands.size()) {
+ int j = i;
+
+ // Advance while commands share the same shader
+ while (i < m_commands.size() && m_commands[j]->m_shaderDna == m_commands[i]->m_shaderDna)
+ ++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.at(j)->m_parameterPack.m_uniforms;
+ PackUniformHash::iterator it = uniforms.begin();
+ const PackUniformHash::iterator end = uniforms.end();
+
+ while (it != end) {
+ // 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 UniformValue refValue = cachedUniforms.value(it.key());
+ if (it.value() == refValue) {
+ it = uniforms.erase(it);
+ } else {
+ cachedUniforms.insert(it.key(), it.value());
+ ++it;
+ }
+ }
+ ++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
+QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities) const
+{
+ // 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);
+
+ QVector<RenderCommand *> commands;
+ commands.reserve(entities.size());
+
+ for (Entity *entity : entities) {
+ 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->lookupHandle<Geometry, GeometryManager, HGeometry>(geometryRenderer->geometryId());
+ Geometry *geometry = m_manager->data<Geometry, GeometryManager>(geometryHandle);
+
+ // 1 RenderCommand per RenderPass pass on an Entity with a Mesh
+ for (const RenderPassParameterData &passData : renderPassData) {
+ // Add the RenderPass Parameters
+ RenderCommand *command = new RenderCommand();
+
+ // 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);
+
+ command->m_geometry = geometryHandle;
+ command->m_geometryRenderer = geometryRendererHandle;
+ 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 = new RenderStateSet();
+ addToRenderStateSet(command->m_stateSet, 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);
+ }
+
+ // 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 = 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;
+ });
+ }
+
+ ParameterInfoList globalParameters = passData.parameterInfo;
+ // setShaderAndUniforms can initialize a localData
+ // make sure this is cleared before we leave this function
+ setShaderAndUniforms(command,
+ pass,
+ globalParameters,
+ entity,
+ lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)),
+ m_environmentLight);
+
+ // Store all necessary information for actual drawing if command is valid
+ command->m_isValid = !command->m_attributes.empty();
+ if (command->m_isValid) {
+ // Update the draw command with what's going to be needed for the drawing
+ uint primitiveCount = geometryRenderer->vertexCount();
+ uint estimatedCount = 0;
+ Attribute *indexAttribute = nullptr;
+
+ const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes();
+ for (Qt3DCore::QNodeId attributeId : attributeIds) {
+ Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId);
+ if (attribute->attributeType() == QAttribute::IndexAttribute)
+ indexAttribute = attribute;
+ else if (command->m_attributes.contains(attribute->nameId()))
+ estimatedCount = qMax(attribute->count(), estimatedCount);
+ }
+
+ // Update the draw command with all the information required for the drawing
+ command->m_drawIndexed = (indexAttribute != nullptr);
+ if (command->m_drawIndexed) {
+ command->m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType());
+ command->m_indexAttributeByteOffset = indexAttribute->byteOffset();
+ }
+
+ // Use the count specified by the GeometryRender
+ // If not specified 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();
+ }
+
+ commands.append(command);
+ }
+ }
+ }
+
+ // We reset the local data once we are done with it
+ m_localData.setLocalData(nullptr);
+
+ return commands;
+}
+
+QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities) const
+{
+ // 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);
+
+ // 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/
+ QVector<RenderCommand *> commands;
+ commands.reserve(entities.size());
+ for (Entity *entity : entities) {
+ ComputeCommand *computeJob = nullptr;
+ if ((computeJob = entity->renderComponent<ComputeCommand>()) != 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
+ ParameterInfoList globalParameters = passData.parameterInfo;
+ RenderPass *pass = passData.pass;
+ parametersFromParametersProvider(&globalParameters, m_manager->parameterManager(), pass);
+
+ RenderCommand *command = new RenderCommand();
+ 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());
+ setShaderAndUniforms(command,
+ pass,
+ globalParameters,
+ entity,
+ QVector<LightSource>(),
+ nullptr);
+ commands.append(command);
+ }
+ }
+ }
+
+ // We reset the local data once we are done with it
+ m_localData.setLocalData(nullptr);
+
+ return commands;
+}
+
+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
+ // 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);
+ for (int i = 0; i < uniformArraySize; ++i) {
+ const Qt3DCore::QNodeId texId = nodeIds[i];
+ const Texture *tex = m_manager->textureManager()->lookupResource(texId);
+ if (tex != nullptr)
+ uniformPack.setTexture(nameId, i, texId);
+ }
+
+ UniformValue textureValue(uniformArraySize * sizeof(int), UniformValue::TextureValue);
+ std::fill(textureValue.data<int>(), textureValue.data<int>() + uniformArraySize, -1);
+ uniformPack.setUniform(nameId, textureValue);
+ } else {
+ uniformPack.setUniform(nameId, value);
+ }
+}
+
+void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack,
+ int glslNameId,
+ int nameId,
+ Entity *entity,
+ const Matrix4x4 &worldTransform) const
+{
+ uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform));
+}
+
+void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack,
+ Shader *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
+ }
+
+
+ //ShaderData *shaderData = nullptr;
+ // if ((shaderData = m_manager->shaderDataManager()->lookupResource(value.value<Qt3DCore::QNodeId>())) != nullptr) {
+ // UBO are indexed by <ShaderId, ShaderDataId> so that a same QShaderData can be used among different shaders
+ // while still making sure that if they have a different layout everything will still work
+ // If two shaders define the same block with the exact same layout, in that case the UBO could be shared
+ // but how do we know that ? We'll need to compare ShaderUniformBlocks
+
+ // Note: we assume that if a buffer is shared across multiple shaders
+ // then it implies that they share the same layout
+
+ // Temporarly disabled
+
+ // BufferShaderKey uboKey(shaderData->peerId(),
+ // shader->peerId());
+
+ // BlockToUBO uniformBlockUBO;
+ // uniformBlockUBO.m_blockIndex = block.m_index;
+ // uniformBlockUBO.m_shaderDataID = shaderData->peerId();
+ // bool uboNeedsUpdate = false;
+
+ // // build UBO at uboId if not created before
+ // if (!m_manager->glBufferManager()->contains(uboKey)) {
+ // m_manager->glBufferManager()->getOrCreateResource(uboKey);
+ // uboNeedsUpdate = true;
+ // }
+
+ // // If shaderData has been updated (property has changed or one of the nested properties has changed)
+ // // foreach property defined in the QShaderData, we try to fill the value of the corresponding active uniform(s)
+ // // for all the updated properties (all the properties if the UBO was just created)
+ // if (shaderData->updateViewTransform(*m_data->m_viewMatrix) || uboNeedsUpdate) {
+ // // Clear previous values remaining in the hash
+ // m_data->m_uniformBlockBuilder.activeUniformNamesToValue.clear();
+ // // Update only update properties if uboNeedsUpdate is true, otherwise update the whole block
+ // m_data->m_uniformBlockBuilder.updatedPropertiesOnly = uboNeedsUpdate;
+ // // Retrieve names and description of each active uniforms in the uniform block
+ // m_data->m_uniformBlockBuilder.uniforms = shader->activeUniformsForUniformBlock(block.m_index);
+ // // Builds the name-value map for the block
+ // m_data->m_uniformBlockBuilder.buildActiveUniformNameValueMapStructHelper(shaderData, block.m_name);
+ // if (!uboNeedsUpdate)
+ // shaderData->markDirty();
+ // // copy the name-value map into the BlockToUBO
+ // uniformBlockUBO.m_updatedProperties = m_data->m_uniformBlockBuilder.activeUniformNamesToValue;
+ // uboNeedsUpdate = true;
+ // }
+
+ // uniformBlockUBO.m_needsUpdate = uboNeedsUpdate;
+ // uniformPack.setUniformBuffer(std::move(uniformBlockUBO));
+ // }
+ }
+}
+
+void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack,
+ Shader *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();
+ uniformPack.setShaderStorageBuffer(shaderStorageBlock);
+ // Buffer update to GL buffer will be done at render time
+ }
+ }
+}
+
+void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, Shader *shader, ShaderData *shaderData, const QString &structName) const
+{
+ UniformBlockValueBuilder *builder = m_localData.localData();
+ builder->activeUniformNamesToValue.clear();
+
+ // Set the view matrix to be used to transform "Transformed" properties in the ShaderData
+ builder->viewMatrix = m_data.m_viewMatrix;
+ // Force to update the whole block
+ builder->updatedPropertiesOnly = false;
+ // Retrieve names and description of each active uniforms in the uniform block
+ builder->uniforms = shader->activeUniformsForUniformBlock(-1);
+ // Build name-value map for the block
+ builder->buildActiveUniformNameValueMapStructHelper(shaderData, structName);
+ // Set uniform values for each entrie of the block name-value map
+ QHash<int, QVariant>::const_iterator activeValuesIt = builder->activeUniformNamesToValue.constBegin();
+ const QHash<int, QVariant>::const_iterator activeValuesEnd = builder->activeUniformNamesToValue.constEnd();
+
+ // TO DO: Make the ShaderData store UniformValue
+ while (activeValuesIt != activeValuesEnd) {
+ setUniformValue(uniformPack, activeValuesIt.key(), UniformValue::fromVariant(activeValuesIt.value()));
+ ++activeValuesIt;
+ }
+}
+
+void RenderView::setShaderAndUniforms(RenderCommand *command,
+ RenderPass *rPass,
+ 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
+
+ if (rPass != nullptr) {
+ // Index Shader by Shader UUID
+ command->m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(rPass->shaderProgram());
+ Shader *shader = m_manager->data<Shader, ShaderManager>(command->m_shader);
+ if (shader != nullptr && shader->isLoaded()) {
+ command->m_shaderDna = shader->dna();
+
+ // 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> uniformBlockNamesIds = shader->uniformBlockNamesIds();
+ const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds();
+ const QVector<int> attributeNamesIds = shader->attributeNamesIds();
+
+ // Set fragData Name and index
+ // Later on we might want to relink the shader if attachments have changed
+ // But for now we set them once and for all
+ QHash<QString, int> fragOutputs;
+ if (!m_renderTarget.isNull() && !shader->isLoaded()) {
+ 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);
+ }
+ }
+
+ if (!uniformNamesIds.isEmpty() || !attributeNamesIds.isEmpty() ||
+ !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) {
+
+ // Set default standard uniforms without bindings
+ const Matrix4x4 worldTransform = *(entity->worldTransform());
+ for (const int uniformNameId : uniformNamesIds) {
+ if (ms_standardUniformSetters.contains(uniformNameId))
+ setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform);
+ }
+
+ // Set default attributes
+ for (const int attributeNameId : attributeNamesIds)
+ command->m_attributes.push_back(attributeNameId);
+
+ // Parameters remaining could be
+ // -> uniform scalar / vector
+ // -> uniform struct / arrays
+ // -> uniform block / array (4.3)
+ // -> ssbo block / array (4.3)
+
+ ParameterInfoList::const_iterator it = parameters.cbegin();
+ const ParameterInfoList::const_iterator parametersEnd = parameters.cend();
+
+ while (it != parametersEnd) {
+ Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
+ const UniformValue &uniformValue = param->uniformValue();
+ if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform
+ setUniformValue(command->m_parameterPack, it->nameId, uniformValue);
+ } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block
+ setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue);
+ } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO
+ setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue);
+ } else { // Parameter is a struct
+ ShaderData *shaderData = nullptr;
+ if (uniformValue.valueType() == UniformValue::NodeId &&
+ (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) {
+ // Try to check if we have a struct or array matching a QShaderData parameter
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId));
+ }
+ // Otherwise: param unused by current shader
+ }
+ ++it;
+ }
+
+ // Lights
+
+ int lightIdx = 0;
+ for (const LightSource &lightSource : activeLightSources) {
+ if (lightIdx == MAX_LIGHTS)
+ break;
+ Entity *lightEntity = lightSource.entity;
+ const Vector3D worldPos = lightEntity->worldBoundingVolume()->center();
+ 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 (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID))
+ setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax(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()) {
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData());
+ if (shaderData) {
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight"));
+ envLightCount = 1;
+ }
+ }
+ setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount);
+ }
+ // Set frag outputs in the shaders if hash not empty
+ if (!fragOutputs.isEmpty())
+ shader->setFragOutputs(fragOutputs);
+ }
+ }
+ else {
+ qCWarning(Render::Backend) << Q_FUNC_INFO << "Using default effect as none was provided";
+ }
+}
+
+bool RenderView::hasBlitFramebufferInfo() const
+{
+ return m_hasBlitFramebufferInfo;
+}
+
+void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo)
+{
+ m_hasBlitFramebufferInfo = hasBlitFramebufferInfo;
+}
+
+BlitFramebufferInfo RenderView::blitFrameBufferInfo() const
+{
+ return m_blitFrameBufferInfo;
+}
+
+void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo)
+{
+ m_blitFrameBufferInfo = blitFrameBufferInfo;
+}
+
+bool RenderView::isDownloadBuffersEnable() const
+{
+ return m_isDownloadBuffersEnable;
+}
+
+void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable)
+{
+ m_isDownloadBuffersEnable = isDownloadBuffersEnable;
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/renderview_p.h b/src/render/renderers/opengl/renderer/renderview_p.h
new file mode 100644
index 000000000..cb3c74917
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderview_p.h
@@ -0,0 +1,392 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERVIEW_H
+#define QT3DRENDER_RENDER_RENDERVIEW_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/qlayerfilter.h>
+#include <Qt3DRender/private/renderer_p.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 <Qt3DCore/private/qframeallocator_p.h>
+#include <Qt3DRender/private/aligned_malloc_p.h>
+
+// TODO: Move out once this is all refactored
+#include <Qt3DRender/private/renderviewjobutils_p.h>
+
+#include <QVector>
+#include <QSurface>
+#include <QMutex>
+#include <QColor>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QRenderPass;
+
+namespace Render {
+
+class Renderer;
+class NodeManagers;
+class RenderCommand;
+class RenderPassFilter;
+class TechniqueFilter;
+class ViewportNode;
+class Effect;
+class RenderPass;
+
+typedef QPair<ShaderUniform, QVariant> ActivePropertyContent;
+typedef QPair<QString, ActivePropertyContent > ActiveProperty;
+
+struct Q_AUTOTEST_EXPORT Plane
+{
+ explicit Plane(const Vector4D &planeEquation)
+ : planeEquation(planeEquation)
+ , normal(Vector3D(planeEquation).normalized())
+ , d(planeEquation.w() / Vector3D(planeEquation).length())
+ {}
+
+ const Vector4D planeEquation;
+ const Vector3D normal;
+ const float d;
+};
+
+struct Q_AUTOTEST_EXPORT ClearBufferInfo
+{
+ int drawBufferIndex = 0;
+ QRenderTargetOutput::AttachmentPoint attchmentPoint = QRenderTargetOutput::Color0;
+ QVector4D clearColor;
+};
+
+struct Q_AUTOTEST_EXPORT BlitFramebufferInfo
+{
+ Qt3DCore::QNodeId sourceRenderTargetId;
+ Qt3DCore::QNodeId destinationRenderTargetId;
+ QRect sourceRect;
+ QRect destinationRect;
+ Qt3DRender::QRenderTargetOutput::AttachmentPoint sourceAttachmentPoint;
+ Qt3DRender::QRenderTargetOutput::AttachmentPoint destinationAttachmentPoint;
+ QBlitFramebuffer::InterpolationMethod interpolationMethod;
+};
+
+// This class is kind of analogous to RenderBin but I want to avoid trampling
+// on that until we get this working
+
+class Q_AUTOTEST_EXPORT RenderView
+{
+public:
+ RenderView();
+ ~RenderView();
+
+ QT3D_ALIGNED_MALLOC_AND_FREE()
+
+ // TODO: Add a way to specify a sort predicate for the RenderCommands
+ void sort();
+
+ void setRenderer(Renderer *renderer);
+ inline void setSurfaceSize(const QSize &size) Q_DECL_NOTHROW { m_surfaceSize = size; }
+ inline Renderer *renderer() const Q_DECL_NOTHROW { return m_renderer; }
+ inline NodeManagers *nodeManagers() const Q_DECL_NOTHROW { return m_manager; }
+ inline const QSize &surfaceSize() const Q_DECL_NOTHROW { return m_surfaceSize; }
+ inline void setDevicePixelRatio(qreal r) Q_DECL_NOTHROW { m_devicePixelRatio = r; }
+ inline qreal devicePixelRatio() const Q_DECL_NOTHROW { return m_devicePixelRatio; }
+
+ inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_data.m_renderCameraLens = renderCameraLens; }
+ inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_data.m_renderCameraLens; }
+
+ inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_data.m_renderCameraNode = renderCameraNode; }
+ inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_data.m_renderCameraNode; }
+
+ inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_data.m_viewMatrix = viewMatrix; }
+ inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; }
+
+ inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_data.m_viewProjectionMatrix = viewProjectionMatrix; }
+ inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_data.m_viewProjectionMatrix; }
+
+ inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; }
+ inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; }
+
+ inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_data.m_eyeViewDir = dir; }
+ inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; }
+
+ inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_data.m_layerFilterIds.push_back(layerFilterId); }
+ inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_data.m_layerFilterIds; }
+
+ inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_data.m_proximityFilterIds.push_back(proximityFilterId); }
+ inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_data.m_proximityFilterIds; }
+
+ inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_data.m_passFilter = rpFilter; }
+ inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_data.m_passFilter; }
+
+ inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_data.m_techniqueFilter = filter; }
+ inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_data.m_techniqueFilter; }
+
+ inline RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet; }
+ void setStateSet(RenderStateSet *stateSet) Q_DECL_NOTHROW { m_stateSet = stateSet; }
+
+ inline bool noDraw() const Q_DECL_NOTHROW { return m_noDraw; }
+ void setNoDraw(bool noDraw) Q_DECL_NOTHROW { m_noDraw = noDraw; }
+
+ inline bool isCompute() const Q_DECL_NOTHROW { return m_compute; }
+ void setCompute(bool compute) Q_DECL_NOTHROW { m_compute = compute; }
+
+ void setComputeWorkgroups(int x, int y, int z) Q_DECL_NOTHROW { m_workGroups[0] = x; m_workGroups[1] = y; m_workGroups[2] = z; }
+ const int *computeWorkGroups() const Q_DECL_NOTHROW { return m_workGroups; }
+ inline bool frustumCulling() const Q_DECL_NOTHROW { return m_frustumCulling; }
+ void setFrustumCulling(bool frustumCulling) Q_DECL_NOTHROW { m_frustumCulling = frustumCulling; }
+
+ inline void setMaterialParameterTable(const MaterialParameterGathererData &parameters) Q_DECL_NOTHROW { m_parameters = parameters; }
+
+ // TODO: Get rid of this overly complex memory management by splitting out the
+ // InnerData as a RenderViewConfig struct. This can be created by setRenderViewConfigFromFrameGraphLeafNode
+ // and passed along with the RenderView to the functions that populate the renderview
+ inline void setViewport(const QRectF &vp) Q_DECL_NOTHROW { m_viewport = vp; }
+ inline QRectF viewport() const Q_DECL_NOTHROW { return m_viewport; }
+
+ inline float gamma() const Q_DECL_NOTHROW { return m_gamma; }
+ inline void setGamma(float gamma) Q_DECL_NOTHROW { m_gamma = gamma; }
+
+ // depth and stencil ClearBuffers are cached locally
+ // color ClearBuffers are collected, as there may be multiple
+ // color buffers to be cleared. we need to apply all these at rendering
+ void addClearBuffers(const ClearBuffers *cb);
+ inline QVector<ClearBufferInfo> specificClearColorBufferInfo() const { return m_specificClearColorBuffers; }
+ inline QVector<ClearBufferInfo> &specificClearColorBufferInfo() { return m_specificClearColorBuffers; }
+ inline ClearBufferInfo globalClearColorBufferInfo() const { return m_globalClearColorBuffer; }
+
+ inline QClearBuffers::BufferTypeFlags clearTypes() const { return m_clearBuffer; }
+ inline float clearDepthValue() const { return m_clearDepthValue; }
+ inline int clearStencilValue() const { return m_clearStencilValue; }
+
+ RenderPassList passesAndParameters(ParameterInfoList *parameter, Entity *node, bool useDefaultMaterials = true);
+
+ QVector<RenderCommand *> buildDrawRenderCommands(const QVector<Entity *> &entities) const;
+ QVector<RenderCommand *> buildComputeRenderCommands(const QVector<Entity *> &entities) const;
+ void setCommands(QVector<RenderCommand *> &commands) Q_DECL_NOTHROW { m_commands = commands; }
+ QVector<RenderCommand *> commands() const Q_DECL_NOTHROW { return m_commands; }
+
+ void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; }
+ const AttachmentPack &attachmentPack() const { return m_attachmentPack; }
+
+ void setRenderTargetId(Qt3DCore::QNodeId renderTargetId) Q_DECL_NOTHROW { m_renderTarget = renderTargetId; }
+ Qt3DCore::QNodeId renderTargetId() const Q_DECL_NOTHROW { return m_renderTarget; }
+
+ void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_data.m_sortingTypes.append(sortTypes); }
+
+ void setSurface(QSurface *surface) { m_surface = surface; }
+ QSurface *surface() const { return m_surface; }
+
+ void setLightSources(const QVector<LightSource> &lightSources) Q_DECL_NOTHROW { m_lightSources = lightSources; }
+ void setEnvironmentLight(EnvironmentLight *environmentLight) Q_DECL_NOTHROW { m_environmentLight = environmentLight; }
+
+ void updateMatrices();
+
+ inline void setRenderCaptureNodeId(const Qt3DCore::QNodeId nodeId) Q_DECL_NOTHROW { m_renderCaptureNodeId = nodeId; }
+ inline const Qt3DCore::QNodeId renderCaptureNodeId() const Q_DECL_NOTHROW { return m_renderCaptureNodeId; }
+ inline void setRenderCaptureRequest(const QRenderCaptureRequest& request) Q_DECL_NOTHROW { m_renderCaptureRequest = request; }
+ inline const QRenderCaptureRequest renderCaptureRequest() const Q_DECL_NOTHROW { return m_renderCaptureRequest; }
+
+ void setMemoryBarrier(QMemoryBarrier::Operations barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; }
+ QMemoryBarrier::Operations memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; }
+
+ // Helps making the size of RenderView smaller
+ // Contains all the data needed for the actual building of the RenderView
+ // But that aren't used later by the Renderer
+ struct InnerData {
+ InnerData()
+ : m_renderCameraLens(nullptr)
+ , m_renderCameraNode(nullptr)
+ , m_techniqueFilter(nullptr)
+ , m_passFilter(nullptr)
+ {
+ }
+ CameraLens *m_renderCameraLens;
+ Entity *m_renderCameraNode;
+ const TechniqueFilter *m_techniqueFilter;
+ const RenderPassFilter *m_passFilter;
+ Matrix4x4 m_viewMatrix;
+ Matrix4x4 m_viewProjectionMatrix;
+ Qt3DCore::QNodeIdVector m_layerFilterIds;
+ QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes;
+ Vector3D m_eyePos;
+ Vector3D m_eyeViewDir;
+ Qt3DCore::QNodeIdVector m_proximityFilterIds;
+ };
+
+ bool isDownloadBuffersEnable() const;
+ void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable);
+
+ BlitFramebufferInfo blitFrameBufferInfo() const;
+ void setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo);
+
+ bool hasBlitFramebufferInfo() const;
+ void setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo);
+
+private:
+ void setShaderAndUniforms(RenderCommand *command,
+ RenderPass *pass,
+ ParameterInfoList &parameters,
+ Entity *entity,
+ const QVector<LightSource> &activeLightSources,
+ EnvironmentLight *environmentLight) const;
+ mutable QThreadStorage<UniformBlockValueBuilder*> m_localData;
+
+ Qt3DCore::QNodeId m_renderCaptureNodeId;
+ QRenderCaptureRequest m_renderCaptureRequest;
+ bool m_isDownloadBuffersEnable;
+
+ bool m_hasBlitFramebufferInfo;
+ BlitFramebufferInfo m_blitFrameBufferInfo;
+
+ Renderer *m_renderer;
+ NodeManagers *m_manager;
+ QSize m_surfaceSize;
+ qreal m_devicePixelRatio;
+
+ InnerData m_data;
+
+ QRectF m_viewport;
+ float m_gamma;
+ Qt3DCore::QNodeId m_renderTarget;
+ QSurface *m_surface;
+ AttachmentPack m_attachmentPack;
+ QClearBuffers::BufferTypeFlags m_clearBuffer;
+ float m_clearDepthValue;
+ int m_clearStencilValue;
+ ClearBufferInfo m_globalClearColorBuffer; // global ClearColor
+ QVector<ClearBufferInfo> m_specificClearColorBuffers; // different draw buffers with distinct colors
+ RenderStateSet *m_stateSet;
+ bool m_noDraw:1;
+ bool m_compute:1;
+ bool m_frustumCulling:1;
+ int m_workGroups[3];
+ QMemoryBarrier::Operations m_memoryBarrier;
+
+ // We do not use pointers to RenderNodes or Drawable's here so that the
+ // render aspect is free to change the drawables on the next frame whilst
+ // the render thread is submitting these commands.
+ QVector<RenderCommand *> m_commands;
+ mutable QVector<LightSource> m_lightSources;
+ EnvironmentLight *m_environmentLight;
+
+ MaterialParameterGathererData m_parameters;
+
+ enum StandardUniform
+ {
+ ModelMatrix,
+ ViewMatrix,
+ ProjectionMatrix,
+ ModelViewMatrix,
+ ViewProjectionMatrix,
+ ModelViewProjectionMatrix,
+ InverseModelMatrix,
+ InverseViewMatrix,
+ InverseProjectionMatrix,
+ InverseModelViewMatrix,
+ InverseViewProjectionMatrix,
+ InverseModelViewProjectionMatrix,
+ ModelNormalMatrix,
+ ModelViewNormalMatrix,
+ ViewportMatrix,
+ InverseViewportMatrix,
+ AspectRatio,
+ Time,
+ Exposure,
+ Gamma,
+ EyePosition,
+ SkinningPalette
+ };
+
+ typedef QHash<int, StandardUniform> StandardUniformsNameToTypeHash;
+ static StandardUniformsNameToTypeHash ms_standardUniformSetters;
+ static StandardUniformsNameToTypeHash initializeStandardUniformSetters();
+
+ UniformValue standardUniformValue(StandardUniform standardUniformType,
+ Entity *entity,
+ const Matrix4x4 &model) const;
+
+ void setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const;
+ void setStandardUniformValue(ShaderParameterPack &uniformPack,
+ int glslNameId,
+ int nameId,
+ Entity *entity,
+ const Matrix4x4 &worldTransform) const;
+ void setUniformBlockValue(ShaderParameterPack &uniformPack,
+ Shader *shader,
+ const ShaderUniformBlock &block,
+ const UniformValue &value) const;
+ void setShaderStorageValue(ShaderParameterPack &uniformPack,
+ Shader *shader,
+ const ShaderStorageBlock &block,
+ const UniformValue &value) const;
+ void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
+ Shader *shader,
+ ShaderData *shaderData,
+ const QString &structName) const;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERVIEW_H
diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp
new file mode 100644
index 000000000..d08bd6dd4
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp
@@ -0,0 +1,672 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderviewbuilder_p.h"
+
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+const int RenderViewBuilder::m_optimalParallelJobCount = std::max(QThread::idealThreadCount(), 2);
+
+namespace {
+
+class SyncRenderViewCommandBuilders
+{
+public:
+ explicit SyncRenderViewCommandBuilders(const RenderViewInitializerJobPtr &renderViewJob,
+ const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs,
+ Renderer *renderer)
+ : m_renderViewJob(renderViewJob)
+ , m_renderViewBuilderJobs(renderViewBuilderJobs)
+ , m_renderer(renderer)
+ {}
+
+ void operator()()
+ {
+ // Append all the commands and sort them
+ RenderView *rv = m_renderViewJob->renderView();
+
+ int totalCommandCount = 0;
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs))
+ totalCommandCount += renderViewCommandBuilder->commands().size();
+
+ QVector<RenderCommand *> commands;
+ commands.reserve(totalCommandCount);
+
+ // Reduction
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs))
+ commands += std::move(renderViewCommandBuilder->commands());
+ rv->setCommands(commands);
+
+ // 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<RenderViewBuilderJobPtr> m_renderViewBuilderJobs;
+ Renderer *m_renderer;
+};
+
+class SyncFrustumCulling
+{
+public:
+ explicit SyncFrustumCulling(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 SyncRenderViewInitialization
+{
+public:
+ explicit SyncRenderViewInitialization(const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterLayerEntityJobPtr &filterEntityByLayerJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs)
+ : m_renderViewJob(renderViewJob)
+ , m_frustumCullingJob(frustumCullingJob)
+ , m_filterEntityByLayerJob(filterEntityByLayerJob)
+ , m_filterProximityJob(filterProximityJob)
+ , m_materialGathererJobs(materialGathererJobs)
+ , m_renderViewBuilderJobs(renderViewBuilderJobs)
+ {}
+
+ 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
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs))
+ 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<RenderViewBuilderJobPtr> m_renderViewBuilderJobs;
+};
+
+class SyncRenderCommandBuilding
+{
+public:
+ explicit SyncRenderCommandBuilding(const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const LightGathererPtr &lightGathererJob,
+ const RenderableEntityFilterPtr &renderableEntityFilterJob,
+ const ComputableEntityFilterPtr &computableEntityFilterJob,
+ const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs,
+ Renderer *renderer,
+ FrameGraphNode *leafNode)
+ : m_renderViewJob(renderViewJob)
+ , m_frustumCullingJob(frustumCullingJob)
+ , m_filterProximityJob(filterProximityJob)
+ , m_lightGathererJob(lightGathererJob)
+ , m_renderableEntityFilterJob(renderableEntityFilterJob)
+ , m_computableEntityFilterJob(computableEntityFilterJob)
+ , m_materialGathererJobs(materialGathererJobs)
+ , m_renderViewBuilderJobs(renderViewBuilderJobs)
+ , m_renderer(renderer)
+ , m_leafNode(leafNode)
+ {}
+
+ void operator()()
+ {
+ // Set the result of previous job computations
+ // for final RenderCommand building
+ RenderView *rv = m_renderViewJob->renderView();
+
+ if (!rv->noDraw()) {
+ rv->setEnvironmentLight(m_lightGathererJob->takeEnvironmentLight());
+
+ // We sort the vector so that the removal can then be performed linearly
+
+ QVector<Entity *> renderableEntities;
+ const bool isDraw = !rv->isCompute();
+ if (isDraw)
+ renderableEntities = std::move(m_renderableEntityFilterJob->filteredEntities());
+ else
+ renderableEntities = std::move(m_computableEntityFilterJob->filteredEntities());
+
+ // Filter out entities that weren't selected by the layer filters
+ std::sort(renderableEntities.begin(), renderableEntities.end());
+
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ const QVector<Entity *> filteredEntities = m_renderer->cache()->leafNodeCache.value(m_leafNode).filterEntitiesByLayer;
+ lock.unlock();
+ // 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.
+ QVector<LightSource> lightSources = m_lightGathererJob->lights();
+ for (int i = 0; i < lightSources.count(); ++i) {
+ if (!filteredEntities.contains(lightSources[i].entity))
+ lightSources.removeAt(i--);
+ }
+ rv->setLightSources(lightSources);
+
+ 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
+ renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities());
+ }
+
+ // Split among the number of command builders
+ int i = 0;
+ const int m = RenderViewBuilder::optimalJobCount() - 1;
+ const int packetSize = renderableEntities.size() / RenderViewBuilder::optimalJobCount();
+ while (i < m) {
+ const RenderViewBuilderJobPtr renderViewCommandBuilder = m_renderViewBuilderJobs.at(i);
+ renderViewCommandBuilder->setRenderables(renderableEntities.mid(i * packetSize, packetSize));
+ ++i;
+ }
+ m_renderViewBuilderJobs.at(i)->setRenderables(renderableEntities.mid(i * packetSize, packetSize + renderableEntities.size() % (m + 1)));
+ {
+ QMutexLocker rendererCacheLock(m_renderer->cache()->mutex());
+ rv->setMaterialParameterTable(m_renderer->cache()->leafNodeCache.value(m_leafNode).materialParameterGatherer);
+ }
+ }
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+ LightGathererPtr m_lightGathererJob;
+ RenderableEntityFilterPtr m_renderableEntityFilterJob;
+ ComputableEntityFilterPtr m_computableEntityFilterJob;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+ QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+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_renderViewJob(RenderViewInitializerJobPtr::create())
+ , m_filterEntityByLayerJob()
+ , m_lightGathererJob(Render::LightGathererPtr::create())
+ , m_renderableEntityFilterJob(RenderableEntityFilterPtr::create())
+ , m_computableEntityFilterJob(ComputableEntityFilterPtr::create())
+ , m_frustumCullingJob(new Render::FrustumCullingJob())
+ , m_syncFrustumCullingJob(SynchronizerJobPtr::create(SyncFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling))
+ , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex))
+ , m_syncFilterEntityByLayerJob()
+ , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create())
+{
+}
+
+RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
+{
+ return m_renderViewJob;
+}
+
+FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const
+{
+ return m_filterEntityByLayerJob;
+}
+
+LightGathererPtr RenderViewBuilder::lightGathererJob() const
+{
+ return m_lightGathererJob;
+}
+
+RenderableEntityFilterPtr RenderViewBuilder::renderableEntityFilterJob() const
+{
+ return m_renderableEntityFilterJob;
+}
+
+ComputableEntityFilterPtr RenderViewBuilder::computableEntityFilterJob() const
+{
+ return m_computableEntityFilterJob;
+}
+
+FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const
+{
+ return m_frustumCullingJob;
+}
+
+QVector<RenderViewBuilderJobPtr> RenderViewBuilder::renderViewBuilderJobs() const
+{
+ return m_renderViewBuilderJobs;
+}
+
+QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const
+{
+ return m_materialGathererJobs;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewInitializationJob() const
+{
+ return m_syncRenderViewInitializationJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncFrustumCullingJob() const
+{
+ return m_syncFrustumCullingJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderCommandBuildingJob() const
+{
+ return m_syncRenderCommandBuildingJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewCommandBuildersJob() const
+{
+ return m_syncRenderViewCommandBuildersJob;
+}
+
+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
+ EntityManager *entityManager = m_renderer->nodeManagers()->renderNodesManager();
+ m_filterProximityJob->setManager(m_renderer->nodeManagers());
+ m_renderableEntityFilterJob->setManager(entityManager);
+ m_computableEntityFilterJob->setManager(entityManager);
+ m_frustumCullingJob->setRoot(m_renderer->sceneRoot());
+ m_lightGathererJob->setManager(entityManager);
+ 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_renderViewBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto renderViewCommandBuilder = Render::RenderViewBuilderJobPtr::create();
+ renderViewCommandBuilder->setIndex(m_renderViewIndex);
+ renderViewCommandBuilder->setRenderer(m_renderer);
+ m_renderViewBuilderJobs.push_back(renderViewCommandBuilder);
+ }
+
+ if (m_materialGathererCacheNeedsToBeRebuilt) {
+ // Since Material gathering is an heavy task, we split it
+ const QVector<HMaterial> materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles();
+ const int elementsPerJob = materialHandles.size() / RenderViewBuilder::m_optimalParallelJobCount;
+ const int lastRemaingElements = materialHandles.size() % RenderViewBuilder::m_optimalParallelJobCount;
+ m_materialGathererJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto materialGatherer = Render::MaterialParameterGathererJobPtr::create();
+ materialGatherer->setNodeManagers(m_renderer->nodeManagers());
+ materialGatherer->setRenderer(m_renderer);
+ 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_syncRenderCommandBuildingJob = SynchronizerJobPtr::create(SyncRenderCommandBuilding(m_renderViewJob,
+ m_frustumCullingJob,
+ m_filterProximityJob,
+ m_lightGathererJob,
+ m_renderableEntityFilterJob,
+ m_computableEntityFilterJob,
+ m_materialGathererJobs,
+ m_renderViewBuilderJobs,
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncRenderViewCommandBuilding);
+
+ m_syncRenderViewCommandBuildersJob = SynchronizerJobPtr::create(SyncRenderViewCommandBuilders(m_renderViewJob,
+ m_renderViewBuilderJobs,
+ m_renderer),
+ JobTypes::SyncRenderViewCommandBuilder);
+
+ m_syncRenderViewInitializationJob = SynchronizerJobPtr::create(SyncRenderViewInitialization(m_renderViewJob,
+ m_frustumCullingJob,
+ m_filterEntityByLayerJob,
+ m_filterProximityJob,
+ m_materialGathererJobs,
+ m_renderViewBuilderJobs),
+ JobTypes::SyncRenderViewInitialization);
+
+}
+
+QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
+{
+ QVector<Qt3DCore::QAspectJobPtr> jobs;
+
+ jobs.reserve(m_materialGathererJobs.size() + m_renderViewBuilderJobs.size() + 11);
+
+ // Set dependencies
+
+ // Finish the skinning palette job before processing renderviews
+ // TODO: Maybe only update skinning palettes for non-culled entities
+ m_renderViewJob->addDependency(m_renderer->updateSkinningPaletteJob());
+
+ m_syncFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob());
+ m_syncFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob());
+ m_syncFrustumCullingJob->addDependency(m_syncRenderViewInitializationJob);
+
+ m_frustumCullingJob->addDependency(m_renderer->expandBoundingVolumeJob());
+ m_frustumCullingJob->addDependency(m_syncFrustumCullingJob);
+
+ m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewInitializationJob);
+
+ m_syncRenderViewInitializationJob->addDependency(m_renderViewJob);
+
+ m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob());
+ m_filterProximityJob->addDependency(m_syncRenderViewInitializationJob);
+
+ m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob);
+ m_syncRenderCommandBuildingJob->addDependency(m_renderableEntityFilterJob);
+ m_syncRenderCommandBuildingJob->addDependency(m_computableEntityFilterJob);
+ m_syncRenderCommandBuildingJob->addDependency(m_filterProximityJob);
+ m_syncRenderCommandBuildingJob->addDependency(m_lightGathererJob);
+ m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob);
+
+ // Ensure the RenderThread won't be able to process dirtyResources
+ // before they have been completely gathered
+ m_syncRenderCommandBuildingJob->addDependency(m_renderer->introspectShadersJob());
+ m_syncRenderCommandBuildingJob->addDependency(m_renderer->bufferGathererJob());
+ m_syncRenderCommandBuildingJob->addDependency(m_renderer->textureGathererJob());
+
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) {
+ renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob);
+ m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder);
+ }
+
+ m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewCommandBuildersJob);
+ m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob);
+
+ // Add jobs
+ jobs.push_back(m_renderViewJob); // Step 1
+ jobs.push_back(m_renderableEntityFilterJob); // Step 1
+ jobs.push_back(m_lightGathererJob); // Step 1
+
+ // Note: do it only if OpenGL 4.3+ available
+ jobs.push_back(m_computableEntityFilterJob); // Step 1
+
+ jobs.push_back(m_syncRenderViewInitializationJob); // Step 2
+
+ if (m_layerCacheNeedsToBeRebuilt) {
+ m_filterEntityByLayerJob->addDependency(m_syncRenderViewInitializationJob);
+ m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob());
+
+ m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob);
+ m_syncRenderCommandBuildingJob->addDependency(m_syncFilterEntityByLayerJob);
+
+ jobs.push_back(m_filterEntityByLayerJob); // Step 3
+ jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4
+ }
+ jobs.push_back(m_syncFrustumCullingJob); // 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_syncRenderViewInitializationJob);
+ materialGatherer->addDependency(m_renderer->introspectShadersJob());
+ materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob());
+ jobs.push_back(materialGatherer); // Step3
+ m_syncMaterialGathererJob->addDependency(materialGatherer);
+ }
+ m_syncRenderCommandBuildingJob->addDependency(m_syncMaterialGathererJob);
+
+ jobs.push_back(m_syncMaterialGathererJob); // Step 3
+ }
+
+ jobs.push_back(m_frustumCullingJob); // Step 4
+ jobs.push_back(m_syncRenderCommandBuildingJob); // Step 5
+
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 6
+ jobs.push_back(renderViewCommandBuilder);
+
+ jobs.push_back(m_syncRenderViewCommandBuildersJob); // 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;
+}
+
+int RenderViewBuilder::optimalJobCount()
+{
+ return RenderViewBuilder::m_optimalParallelJobCount;
+}
+
+QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset)
+{
+ QVector<Entity *> intersection;
+ intersection.reserve(qMin(entities.size(), subset.size()));
+ std::set_intersection(entities.begin(), entities.end(),
+ subset.begin(), subset.end(),
+ std::back_inserter(intersection));
+
+ return intersection;
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder_p.h b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h
new file mode 100644
index 000000000..818313500
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERVIEWBUILDER_H
+#define QT3DRENDER_RENDER_RENDERVIEWBUILDER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <functional>
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/filterentitybycomponentjob_p.h>
+#include <Qt3DRender/private/filterlayerentityjob_p.h>
+#include <Qt3DRender/private/genericlambdajob_p.h>
+#include <Qt3DRender/private/materialparametergathererjob_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/renderviewbuilderjob_p.h>
+#include <Qt3DRender/private/renderview_p.h>
+#include <Qt3DRender/private/frustumcullingjob_p.h>
+#include <Qt3DRender/private/lightgatherer_p.h>
+#include <Qt3DRender/private/filterproximitydistancejob_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Renderer;
+
+using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+using ComputableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::ComputeCommand, Render::Material>;
+using RenderableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::GeometryRenderer, Render::Material>;
+
+class Q_AUTOTEST_EXPORT RenderViewBuilder
+{
+public:
+ explicit RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer);
+
+ RenderViewInitializerJobPtr renderViewJob() const;
+ FilterLayerEntityJobPtr filterEntityByLayerJob() const;
+ LightGathererPtr lightGathererJob() const;
+ RenderableEntityFilterPtr renderableEntityFilterJob() const;
+ ComputableEntityFilterPtr computableEntityFilterJob() const;
+ FrustumCullingJobPtr frustumCullingJob() const;
+ QVector<RenderViewBuilderJobPtr> renderViewBuilderJobs() const;
+ QVector<MaterialParameterGathererJobPtr> materialGathererJobs() const;
+ SynchronizerJobPtr syncRenderViewInitializationJob() const;
+ SynchronizerJobPtr syncFrustumCullingJob() const;
+ SynchronizerJobPtr syncRenderCommandBuildingJob() const;
+ SynchronizerJobPtr syncRenderViewCommandBuildersJob() 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;
+
+ 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;
+
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ LightGathererPtr m_lightGathererJob;
+ RenderableEntityFilterPtr m_renderableEntityFilterJob;
+ ComputableEntityFilterPtr m_computableEntityFilterJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+
+ SynchronizerJobPtr m_syncRenderViewInitializationJob;
+ SynchronizerJobPtr m_syncFrustumCullingJob;
+ SynchronizerJobPtr m_syncRenderCommandBuildingJob;
+ SynchronizerJobPtr m_syncRenderViewCommandBuildersJob;
+ SynchronizerJobPtr m_setClearDrawBufferIndexJob;
+ SynchronizerJobPtr m_syncFilterEntityByLayerJob;
+ SynchronizerJobPtr m_syncMaterialGathererJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+
+ static const int m_optimalParallelJobCount;
+};
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERVIEWBUILDER_H
diff --git a/src/render/renderers/opengl/renderer/shaderparameterpack.cpp b/src/render/renderers/opengl/renderer/shaderparameterpack.cpp
new file mode 100644
index 000000000..f78e45a5e
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/shaderparameterpack.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "shaderparameterpack_p.h"
+
+#include <Qt3DRender/private/graphicscontext_p.h>
+#include <Qt3DRender/private/texture_p.h>
+
+#include <Qt3DCore/private/qframeallocator_p.h>
+
+#include <QOpenGLShaderProgram>
+#include <QDebug>
+#include <QColor>
+#include <QQuaternion>
+#include <Qt3DRender/private/renderlogging_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+ShaderParameterPack::~ShaderParameterPack()
+{
+ m_uniforms.clear();
+}
+
+void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val)
+{
+ m_uniforms.insert(glslNameId, val);
+}
+
+void ShaderParameterPack::setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId texId)
+{
+ for (int t=0; t<m_textures.size(); ++t) {
+ if (m_textures[t].glslNameId != glslNameId || m_textures[t].uniformArrayIndex != uniformArrayIndex)
+ continue;
+
+ m_textures[t].texId = texId;
+ return;
+ }
+
+ m_textures.append(NamedTexture(glslNameId, texId, uniformArrayIndex));
+}
+
+// Contains Uniform Block Index and QNodeId of the ShaderData (UBO)
+void ShaderParameterPack::setUniformBuffer(BlockToUBO blockToUBO)
+{
+ m_uniformBuffers.append(std::move(blockToUBO));
+}
+
+void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO)
+{
+ m_shaderStorageBuffers.push_back(std::move(blockToSSBO));
+}
+
+void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform)
+{
+ m_submissionUniforms.push_back(uniform);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderer/shaderparameterpack_p.h b/src/render/renderers/opengl/renderer/shaderparameterpack_p.h
new file mode 100644
index 000000000..5703bb17b
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/shaderparameterpack_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H
+#define QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QVariant>
+#include <QByteArray>
+#include <QVector>
+#include <QOpenGLShaderProgram>
+#include <Qt3DCore/qnodeid.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/shadervariables_p.h>
+#include <Qt3DRender/private/uniform_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLShaderProgram;
+
+namespace Qt3DCore {
+class QFrameAllocator;
+}
+
+namespace Qt3DRender {
+namespace Render {
+
+class GraphicsContext;
+
+struct BlockToUBO {
+ int m_blockIndex;
+ Qt3DCore::QNodeId m_bufferID;
+ bool m_needsUpdate;
+ QHash<QString, QVariant> m_updatedProperties;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToUBO, Q_MOVABLE_TYPE)
+
+struct BlockToSSBO {
+ int m_blockIndex;
+ Qt3DCore::QNodeId m_bufferID;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToSSBO, Q_PRIMITIVE_TYPE)
+
+
+typedef QHash<int, UniformValue> PackUniformHash;
+
+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 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 NamedTexture
+ {
+ NamedTexture() {}
+ NamedTexture(const int glslNameId, Qt3DCore::QNodeId texId, int uniformArrayIndex)
+ : glslNameId(glslNameId)
+ , texId(texId)
+ , uniformArrayIndex(uniformArrayIndex)
+ { }
+
+ int glslNameId;
+ Qt3DCore::QNodeId texId;
+ int uniformArrayIndex;
+ };
+
+ inline QVector<NamedTexture> textures() const { return m_textures; }
+ 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<NamedTexture> m_textures;
+ QVector<BlockToUBO> m_uniformBuffers;
+ QVector<BlockToSSBO> m_shaderStorageBuffers;
+ QVector<ShaderUniform> m_submissionUniforms;
+
+ friend class RenderView;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderParameterPack::NamedTexture, Q_PRIMITIVE_TYPE)
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::Render::ShaderParameterPack)
+
+#endif // QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H
diff --git a/src/render/renderers/opengl/renderer/shadervariables_p.h b/src/render/renderers/opengl/renderer/shadervariables_p.h
new file mode 100644
index 000000000..e0fa07dff
--- /dev/null
+++ b/src/render/renderers/opengl/renderer/shadervariables_p.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_SHADERVARIABLES_P_H
+#define QT3DRENDER_RENDER_SHADERVARIABLES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qt3drender_global.h>
+#include <QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+struct ShaderAttribute
+{
+ ShaderAttribute()
+ : m_nameId(-1)
+ , m_type(0)
+ , m_size(0)
+ , m_location(-1)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ GLenum m_type;
+ int m_size;
+ int m_location;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderAttribute, Q_MOVABLE_TYPE)
+
+struct ShaderUniform
+{
+ ShaderUniform()
+ : m_nameId(-1)
+ , m_type(GL_NONE)
+ , m_size(0)
+ , m_offset(-1)
+ , m_location(-1)
+ , m_blockIndex(-1)
+ , m_arrayStride(-1)
+ , m_matrixStride(-1)
+ , m_rawByteSize(0)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ GLenum m_type;
+ int m_size;
+ int m_offset; // -1 default, >= 0 if uniform defined in uniform block
+ int m_location; // -1 if uniform defined in a uniform block
+ int m_blockIndex; // -1 is the default, >= 0 if uniform defined in uniform block
+ int m_arrayStride; // -1 is the default, >= 0 if uniform defined in uniform block and if it's an array
+ int m_matrixStride; // -1 is the default, >= 0 uniform defined in uniform block and is a matrix
+ uint m_rawByteSize; // contains byte size (size / type / strides)
+ // size, offset and strides are in bytes
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniform, Q_MOVABLE_TYPE)
+
+struct ShaderUniformBlock
+{
+ ShaderUniformBlock()
+ : m_nameId(-1)
+ , m_index(-1)
+ , m_binding(-1)
+ , m_activeUniformsCount(0)
+ , m_size(0)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ int m_index;
+ int m_binding;
+ int m_activeUniformsCount;
+ int m_size;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniformBlock, Q_MOVABLE_TYPE)
+
+struct ShaderStorageBlock
+{
+ ShaderStorageBlock()
+ : m_nameId(-1)
+ , m_index(-1)
+ , m_binding(-1)
+ , m_size(0)
+ , m_activeVariablesCount(0)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ int m_index;
+ int m_binding;
+ int m_size;
+ int m_activeVariablesCount;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderStorageBlock, Q_MOVABLE_TYPE)
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_SHADERVARIABLES_P_H
diff --git a/src/render/renderers/opengl/renderstates/renderstates.pri b/src/render/renderers/opengl/renderstates/renderstates.pri
new file mode 100644
index 000000000..10f51a0ed
--- /dev/null
+++ b/src/render/renderers/opengl/renderstates/renderstates.pri
@@ -0,0 +1,7 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/renderstateset.cpp
+
+HEADERS += \
+ $$PWD/renderstateset_p.h
diff --git a/src/render/renderers/opengl/renderstates/renderstateset.cpp b/src/render/renderers/opengl/renderstates/renderstateset.cpp
new file mode 100644
index 000000000..bf84b0e1c
--- /dev/null
+++ b/src/render/renderers/opengl/renderstates/renderstateset.cpp
@@ -0,0 +1,393 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderstateset_p.h"
+
+#include <bitset>
+
+#include <QDebug>
+#include <QOpenGLContext>
+
+#include <Qt3DRender/private/submissioncontext_p.h>
+#include <Qt3DRender/private/renderstates_p.h>
+#include <Qt3DRender/private/qrenderstate_p.h>
+
+#include <Qt3DRender/qalphacoverage.h>
+#include <Qt3DRender/qalphatest.h>
+#include <Qt3DRender/private/qalphatest_p.h>
+#include <Qt3DRender/qblendequation.h>
+#include <Qt3DRender/private/qblendequation_p.h>
+#include <Qt3DRender/qblendequationarguments.h>
+#include <Qt3DRender/private/qblendequationarguments_p.h>
+#include <Qt3DRender/qcolormask.h>
+#include <Qt3DRender/private/qcolormask_p.h>
+#include <Qt3DRender/qcullface.h>
+#include <Qt3DRender/private/qcullface_p.h>
+#include <Qt3DRender/qnodepthmask.h>
+#include <Qt3DRender/qdepthtest.h>
+#include <Qt3DRender/private/qdepthtest_p.h>
+#include <Qt3DRender/qdithering.h>
+#include <Qt3DRender/qfrontface.h>
+#include <Qt3DRender/private/qfrontface_p.h>
+#include <Qt3DRender/qpointsize.h>
+#include <Qt3DRender/private/qpointsize_p.h>
+#include <Qt3DRender/qpolygonoffset.h>
+#include <Qt3DRender/private/qpolygonoffset_p.h>
+#include <Qt3DRender/qscissortest.h>
+#include <Qt3DRender/private/qscissortest_p.h>
+#include <Qt3DRender/qstenciltest.h>
+#include <Qt3DRender/private/qstenciltest_p.h>
+#include <Qt3DRender/qstenciltestarguments.h>
+#include <Qt3DRender/private/qstenciltestarguments_p.h>
+#include <Qt3DRender/qclipplane.h>
+#include <Qt3DRender/private/qclipplane_p.h>
+#include <Qt3DRender/qmultisampleantialiasing.h>
+#include <Qt3DRender/qseamlesscubemap.h>
+#include <Qt3DRender/qstenciloperation.h>
+#include <Qt3DRender/private/qstenciloperation_p.h>
+#include <Qt3DRender/qstenciloperationarguments.h>
+#include <Qt3DRender/private/qstenciloperationarguments_p.h>
+#include <Qt3DRender/qstencilmask.h>
+#include <Qt3DRender/private/qstencilmask_p.h>
+#include <Qt3DRender/qlinewidth.h>
+#include <Qt3DRender/private/qlinewidth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+RenderStateSet::RenderStateSet()
+ : m_stateMask(0)
+{
+}
+
+RenderStateSet::~RenderStateSet()
+{
+}
+
+template<>
+void RenderStateSet::addState<StateVariant>(const StateVariant &ds)
+{
+ m_states.push_back(ds);
+ m_stateMask |= ds.type;
+}
+
+int RenderStateSet::changeCost(RenderStateSet *previousState)
+{
+ if (previousState == this)
+ return 0;
+
+ int cost = 0;
+
+ // first, find cost of any resets
+ StateMaskSet invOurState = ~stateMask();
+ StateMaskSet stateToReset = previousState->stateMask() & invOurState;
+
+ std::bitset<64> bs(stateToReset);
+ cost += int(bs.count());
+
+ // now, find out how many states we're changing
+ for (const StateVariant &ds : qAsConst(m_states)) {
+ // if the other state contains matching, then doesn't
+ // contribute to cost at all
+ if (previousState->contains(ds))
+ continue;
+
+ // flat cost for now; could be replaced with a cost() method on
+ // RenderState
+ cost += 2;
+ }
+
+ return cost;
+}
+
+void RenderStateSet::apply(SubmissionContext *gc)
+{
+ RenderStateSet* previousStates = gc->currentStateSet();
+
+ const StateMaskSet invOurState = ~stateMask();
+ // generate a mask for each set bit in previous, where we do not have
+ // the corresponding bit set.
+
+ StateMaskSet stateToReset = 0;
+ if (previousStates) {
+ stateToReset = previousStates->stateMask() & invOurState;
+ qCDebug(RenderStates) << "previous states " << QString::number(previousStates->stateMask(), 2);
+ }
+ qCDebug(RenderStates) << " current states " << QString::number(stateMask(), 2) << "inverse " << QString::number(invOurState, 2) << " -> states to change: " << QString::number(stateToReset, 2);
+
+ // Reset states that aren't active in the current state set
+ resetMasked(stateToReset, gc);
+
+ // Apply states that weren't in the previous state or that have
+ // different values
+ for (const StateVariant &ds : qAsConst(m_states)) {
+ if (previousStates && previousStates->contains(ds))
+ continue;
+ ds.apply(gc);
+ }
+}
+
+StateMaskSet RenderStateSet::stateMask() const
+{
+ return m_stateMask;
+}
+
+void RenderStateSet::merge(RenderStateSet *other)
+{
+ m_stateMask |= other->stateMask();
+}
+
+void RenderStateSet::resetMasked(StateMaskSet maskOfStatesToReset, SubmissionContext *gc)
+{
+ // TO DO -> Call gcHelper methods instead of raw GL
+ // QOpenGLFunctions shouldn't be used here directly
+ QOpenGLFunctions *funcs = gc->openGLContext()->functions();
+
+ if (maskOfStatesToReset & ScissorStateMask)
+ funcs->glDisable(GL_SCISSOR_TEST);
+
+ if (maskOfStatesToReset & BlendStateMask)
+ funcs->glDisable(GL_BLEND);
+
+ if (maskOfStatesToReset & StencilWriteStateMask)
+ funcs->glStencilMask(0);
+
+ if (maskOfStatesToReset & StencilTestStateMask)
+ funcs->glDisable(GL_STENCIL_TEST);
+
+ if (maskOfStatesToReset & DepthTestStateMask)
+ funcs->glDisable(GL_DEPTH_TEST);
+
+ if (maskOfStatesToReset & DepthWriteStateMask)
+ funcs->glDepthMask(GL_TRUE); // reset to default
+
+ if (maskOfStatesToReset & FrontFaceStateMask)
+ funcs->glFrontFace(GL_CCW); // reset to default
+
+ if (maskOfStatesToReset & CullFaceStateMask)
+ funcs->glDisable(GL_CULL_FACE);
+
+ if (maskOfStatesToReset & DitheringStateMask)
+ funcs->glDisable(GL_DITHER);
+
+ if (maskOfStatesToReset & AlphaCoverageStateMask)
+ gc->setAlphaCoverageEnabled(false);
+
+ if (maskOfStatesToReset & PointSizeMask)
+ gc->pointSize(false, 1.0f); // reset to default
+
+ if (maskOfStatesToReset & PolygonOffsetStateMask)
+ funcs->glDisable(GL_POLYGON_OFFSET_FILL);
+
+ if (maskOfStatesToReset & ColorStateMask)
+ funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ if (maskOfStatesToReset & ClipPlaneMask) {
+ GLint max = gc->maxClipPlaneCount();
+ for (GLint i = 0; i < max; ++i)
+ gc->disableClipPlane(i);
+ }
+
+ if (maskOfStatesToReset & SeamlessCubemapMask)
+ gc->setSeamlessCubemap(false);
+
+ if (maskOfStatesToReset & StencilOpMask)
+ funcs->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ if (maskOfStatesToReset & LineWidthMask)
+ funcs->glLineWidth(1.0f);
+}
+
+bool RenderStateSet::contains(const StateVariant &ds) const
+{
+ // trivial reject using the state mask bits
+ if (!(ds.type & stateMask()))
+ return false;
+
+ for (const StateVariant &rs : m_states) {
+ if (rs == ds)
+ return true;
+ }
+ return false;
+}
+
+StateVariant RenderStateSet::initializeStateFromPeer(const Qt3DRender::QRenderStateCreatedChangeBasePtr change)
+{
+ switch (change->renderStateType()) {
+ case AlphaCoverageStateMask: {
+ return RenderStateSet::createState<AlphaCoverage>();
+ }
+
+ case AlphaTestMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QAlphaTestData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<AlphaFunc>(data.alphaFunction, data.referenceValue);
+ }
+
+ case BlendStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QBlendEquationData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<BlendEquation>(data.blendFunction);
+ }
+
+ case BlendEquationArgumentsMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QBlendEquationArgumentsData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<BlendEquationArguments>(
+ data.sourceRgb, data.destinationRgb,
+ data.sourceAlpha, data.destinationAlpha,
+ change->isNodeEnabled(),
+ data.bufferIndex);
+ }
+
+ case MSAAEnabledStateMask: {
+ return RenderStateSet::createState<MSAAEnabled>(change->isNodeEnabled());
+ }
+
+ case CullFaceStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QCullFaceData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<CullFace>(data.mode);
+ }
+
+ case DepthWriteStateMask: {
+ return RenderStateSet::createState<NoDepthMask>(false);
+ }
+
+ case DepthTestStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QDepthTestData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<DepthTest>(data.depthFunction);
+ }
+
+ case FrontFaceStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QFrontFaceData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<FrontFace>(data.direction);
+ }
+
+ case ScissorStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QScissorTestData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<ScissorTest>(data.left, data.bottom,
+ data.width, data.height);
+ }
+
+ case StencilTestStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QStencilTestData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<StencilTest>(data.front.stencilFunction,
+ data.front.referenceValue,
+ data.front.comparisonMask,
+ data.back.stencilFunction,
+ data.back.referenceValue,
+ data.back.comparisonMask);
+ }
+
+ case PointSizeMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QPointSizeData>>(change);
+ const auto &data = typedChange->data;
+ const bool isProgrammable = (data.sizeMode == QPointSize::Programmable);
+ return RenderStateSet::createState<PointSize>(isProgrammable, data.value);
+ }
+
+ case PolygonOffsetStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QPolygonOffsetData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<PolygonOffset>(data.scaleFactor, data.depthSteps);
+ }
+
+ case ColorStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QColorMaskData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<ColorMask>(data.redMasked, data.greenMasked,
+ data.blueMasked, data.alphaMasked);
+ }
+
+ case ClipPlaneMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QClipPlaneData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<ClipPlane>(data.planeIndex,
+ data.normal,
+ data.distance);
+ }
+
+ case SeamlessCubemapMask: {
+ return RenderStateSet::createState<SeamlessCubemap>();
+ }
+
+ case StencilOpMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QStencilOperationData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<StencilOp>(data.front.stencilTestFailureOperation,
+ data.front.depthTestFailureOperation,
+ data.front.allTestsPassOperation,
+ data.back.stencilTestFailureOperation,
+ data.back.depthTestFailureOperation,
+ data.back.allTestsPassOperation);
+ }
+
+ case StencilWriteStateMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QStencilMaskData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<StencilMask>(data.frontOutputMask,
+ data.backOutputMask);
+ }
+
+ case LineWidthMask: {
+ const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QLineWidthData>>(change);
+ const auto &data = typedChange->data;
+ return RenderStateSet::createState<LineWidth>(data.value, data.smooth);
+ }
+
+ // TODO: Fix Dithering state
+ case DitheringStateMask:
+ default:
+ Q_UNREACHABLE();
+ return StateVariant();
+ }
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/renderstates/renderstateset_p.h b/src/render/renderers/opengl/renderstates/renderstateset_p.h
new file mode 100644
index 000000000..58d46c7a6
--- /dev/null
+++ b/src/render/renderers/opengl/renderstates/renderstateset_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERSTATE_H
+#define QT3DRENDER_RENDER_RENDERSTATE_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/genericstate_p.h>
+#include <Qt3DRender/private/renderstates_p.h>
+#include <Qt3DRender/private/statevariant_p.h>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QRenderState;
+
+namespace Render {
+
+class SubmissionContext;
+class RenderState;
+
+class RenderStateSet
+{
+public:
+ RenderStateSet();
+ ~RenderStateSet();
+
+ template<typename GenericState>
+ void addState(const GenericState &state)
+ {
+ addState(StateVariant::fromValue(state));
+ }
+
+ /**
+ * @brief changeCost - metric of cost to change to this state-set from
+ * a candidate previous state-set. This is used to find an optimal
+ * ordering of state-sets when sending draw commands.
+ * @param previousState
+ * @return
+ */
+ int changeCost(RenderStateSet* previousState);
+
+ void apply(SubmissionContext *gc);
+
+ StateMaskSet stateMask() const;
+ void merge(RenderStateSet *other);
+ void resetMasked(StateMaskSet maskOfStatesToReset, SubmissionContext* gc);
+
+ template<class State, typename ... Args>
+ static StateVariant createState(Args... values)
+ {
+ State state;
+ state.set(values...);
+ return StateVariant::fromValue(state);
+ }
+
+ static StateVariant initializeStateFromPeer(const Qt3DRender::QRenderStateCreatedChangeBasePtr change);
+
+private:
+ /**
+ * @brief contains - check if this set contains a matching piece of state
+ * @param ds
+ * @return
+ */
+ bool contains(const StateVariant &ds) const;
+
+ StateMaskSet m_stateMask;
+ QVector<StateVariant> m_states;
+};
+
+template<>
+void RenderStateSet::addState<StateVariant>(const StateVariant &state);
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERSTATE_H
diff --git a/src/render/renderers/opengl/textures/gltexture.cpp b/src/render/renderers/opengl/textures/gltexture.cpp
new file mode 100644
index 000000000..e94122f67
--- /dev/null
+++ b/src/render/renderers/opengl/textures/gltexture.cpp
@@ -0,0 +1,468 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qhash.h>
+#include "gltexture_p.h"
+
+#include <QDebug>
+#include <QOpenGLFunctions>
+#include <QOpenGLTexture>
+#include <QOpenGLPixelTransferOptions>
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qtexturedata.h>
+#include <Qt3DRender/qtextureimagedata.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/texturedatamanager_p.h>
+#include <Qt3DRender/private/qabstracttexture_p.h>
+#include <Qt3DRender/private/renderbuffer_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <Qt3DCore/qpropertynodeaddedchange.h>
+#include <Qt3DCore/qpropertynoderemovedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+
+GLTexture::GLTexture(TextureDataManager *texDataMgr,
+ TextureImageDataManager *texImgDataMgr,
+ const QTextureGeneratorPtr &texGen,
+ bool unique)
+ : m_unique(unique)
+ , m_gl(nullptr)
+ , m_renderBuffer(nullptr)
+ , m_textureDataManager(texDataMgr)
+ , m_textureImageDataManager(texImgDataMgr)
+ , m_dataFunctor(texGen)
+{
+ // make sure texture generator is executed
+ // this is needed when Texture have the TargetAutomatic
+ // to ensure they are loaded before trying to instantiate the QOpenGLTexture
+ if (!texGen.isNull())
+ m_textureDataManager->requestData(texGen, this);
+}
+
+GLTexture::~GLTexture()
+{
+ destroyGLTexture();
+}
+
+void GLTexture::destroyResources()
+{
+ if (m_dataFunctor)
+ m_textureDataManager->releaseData(m_dataFunctor, this);
+}
+
+void GLTexture::destroyGLTexture()
+{
+ delete m_gl;
+ m_gl = nullptr;
+ delete m_renderBuffer;
+ m_renderBuffer = nullptr;
+
+ m_dirtyFlags.store(0);
+
+ destroyResources();
+}
+
+QOpenGLTexture* GLTexture::getOrCreateGLTexture()
+{
+ QMutexLocker locker(&m_textureMutex);
+ bool needUpload = false;
+
+ // on the first invocation in the render thread, make sure to
+ // evaluate the texture data generator output
+ // (this might change some property values)
+ if (m_dataFunctor && !m_textureData) {
+ m_textureData = m_textureDataManager->getData(m_dataFunctor);
+
+ // if there is a texture generator, most properties will be defined by it
+ if (m_textureData) {
+ if (m_properties.target != QAbstractTexture::TargetAutomatic)
+ qWarning() << "[Qt3DRender::GLTexture] When a texture provides a generator, it's target is expected to be TargetAutomatic";
+
+ m_actualTarget = m_textureData->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.size() > 0) {
+ // Set the mips level based on the first image if autoMipMapGeneration is disabled
+ if (!m_properties.generateMipMaps)
+ m_properties.mipLevels = imageData.first()->mipLevels();
+ }
+
+ setDirtyFlag(Properties, true);
+ needUpload = true;
+ } else {
+ qWarning() << "[Qt3DRender::GLTexture] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame";
+ return nullptr;
+ }
+ }
+
+ // additional texture images may be defined through image data generators
+ if (testDirtyFlag(TextureData)) {
+ m_imageData.clear();
+ needUpload = true;
+
+ int maxMipLevel = 0;
+ for (const Image &img : qAsConst(m_images)) {
+ const QTextureImageDataPtr imgData = m_textureImageDataManager->getData(img.generator);
+
+ Q_ASSERT(imgData);
+ 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);
+ }
+ }
+
+ // don't try to create the texture if the format was not set
+ if (m_properties.format == QAbstractTexture::Automatic)
+ return nullptr;
+
+ // if the properties changed, we need to re-allocate the texture
+ if (testDirtyFlag(Properties)) {
+ delete m_gl;
+ m_gl = nullptr;
+ }
+
+ if (!m_gl) {
+ m_gl = buildGLTexture();
+ if (!m_gl)
+ return nullptr;
+ m_gl->allocateStorage();
+ if (!m_gl->isStorageAllocated()) {
+ return nullptr;
+ }
+ }
+
+ // need to (re-)upload texture data?
+ if (needUpload) {
+ uploadGLTextureData();
+ setDirtyFlag(TextureData, false);
+ }
+
+ // need to set texture parameters?
+ if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) {
+ updateGLTextureParameters();
+ }
+
+ // un-set properties and parameters. The TextureData flag might have been set by another thread
+ // in the meantime, so don't clear that.
+ setDirtyFlag(Properties, false);
+ setDirtyFlag(Parameters, false);
+
+ return m_gl;
+}
+
+RenderBuffer *GLTexture::getOrCreateRenderBuffer()
+{
+ QMutexLocker locker(&m_textureMutex);
+
+ if (m_dataFunctor && !m_textureData) {
+ m_textureData = m_textureDataManager->getData(m_dataFunctor);
+ if (m_textureData) {
+ if (m_properties.target != QAbstractTexture::TargetAutomatic)
+ qWarning() << "[Qt3DRender::GLTexture] [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 {
+ qWarning() << "[Qt3DRender::GLTexture] [renderbuffer] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame";
+ 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;
+}
+
+void GLTexture::setParameters(const TextureParameters &params)
+{
+ QMutexLocker locker(&m_textureMutex);
+ if (m_parameters != params) {
+ m_parameters = params;
+ setDirtyFlag(Parameters);
+ }
+}
+
+void GLTexture::setProperties(const TextureProperties &props)
+{
+ QMutexLocker locker(&m_textureMutex);
+ if (m_properties != props) {
+ m_properties = props;
+ m_actualTarget = props.target;
+ setDirtyFlag(Properties);
+ }
+}
+
+void GLTexture::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;
+ requestUpload();
+ }
+}
+
+void GLTexture::setGenerator(const QTextureGeneratorPtr &generator)
+{
+ // Note: we do not compare if the generator is different
+ // as in some cases we may want to reset the same generator to force a reload
+ // e.g when using remote urls for textures
+ if (m_dataFunctor)
+ m_textureDataManager->releaseData(m_dataFunctor, this);
+
+ m_textureData.reset();
+ m_dataFunctor = generator;
+
+ if (m_dataFunctor) {
+ m_textureDataManager->requestData(m_dataFunctor, this);
+ requestUpload();
+ }
+}
+
+// Return nullptr if
+// - context cannot be obtained
+// - texture hasn't yet been loaded
+QOpenGLTexture *GLTexture::buildGLTexture()
+{
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (!ctx) {
+ qWarning() << Q_FUNC_INFO << "requires an OpenGL context";
+ return nullptr;
+ }
+
+ if (m_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;
+ }
+
+ QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(m_actualTarget));
+
+ // m_format may not be ES2 compatible. Now it's time to convert it, if necessary.
+ QAbstractTexture::TextureFormat format = m_properties.format;
+ if (ctx->isOpenGLES() && ctx->format().majorVersion() < 3) {
+ switch (m_properties.format) {
+ case QOpenGLTexture::RGBA8_UNorm:
+ case QOpenGLTexture::RGBAFormat:
+ format = QAbstractTexture::RGBAFormat;
+ break;
+ case QOpenGLTexture::RGB8_UNorm:
+ case QOpenGLTexture::RGBFormat:
+ format = QAbstractTexture::RGBFormat;
+ break;
+ case QOpenGLTexture::DepthFormat:
+ format = QAbstractTexture::DepthFormat;
+ break;
+ default:
+ qWarning() << Q_FUNC_INFO << "could not find a matching OpenGL ES 2.0 unsized texture format";
+ break;
+ }
+ }
+
+ // Map ETC1 to ETC2 when supported. This allows using features like
+ // immutable storage as ETC2 is standard in GLES 3.0, while the ETC1 extension
+ // is written against GLES 1.0.
+ if (m_properties.format == QAbstractTexture::RGB8_ETC1) {
+ if ((ctx->isOpenGLES() && ctx->format().majorVersion() >= 3)
+ || ctx->hasExtension(QByteArrayLiteral("GL_OES_compressed_ETC2_RGB8_texture"))
+ || ctx->hasExtension(QByteArrayLiteral("GL_ARB_ES3_compatibility")))
+ format = m_properties.format = QAbstractTexture::RGB8_ETC2;
+ }
+
+ glTex->setFormat(m_properties.format == QAbstractTexture::Automatic ?
+ QOpenGLTexture::NoFormat :
+ static_cast<QOpenGLTexture::TextureFormat>(format));
+ glTex->setSize(m_properties.width, m_properties.height, m_properties.depth);
+ // Set layers count if texture array
+ if (m_actualTarget == QAbstractTexture::Target1DArray ||
+ m_actualTarget == QAbstractTexture::Target2DArray ||
+ m_actualTarget == QAbstractTexture::Target3D ||
+ m_actualTarget == QAbstractTexture::Target2DMultisampleArray ||
+ m_actualTarget == QAbstractTexture::TargetCubeMapArray) {
+ glTex->setLayers(m_properties.layers);
+ }
+
+ if (m_actualTarget == QAbstractTexture::Target2DMultisample ||
+ m_actualTarget == QAbstractTexture::Target2DMultisampleArray) {
+ // Set samples count if multisampled texture
+ // (multisampled textures don't have mipmaps)
+ glTex->setSamples(m_properties.samples);
+ } else if (m_properties.generateMipMaps) {
+ glTex->setMipLevels(glTex->maximumMipLevels());
+ } else {
+ glTex->setAutoMipMapGenerationEnabled(false);
+ glTex->setMipBaseLevel(0);
+ glTex->setMipMaxLevel(m_properties.mipLevels - 1);
+ glTex->setMipLevels(m_properties.mipLevels);
+ }
+
+ if (!glTex->create()) {
+ qWarning() << Q_FUNC_INFO << "creating QOpenGLTexture failed";
+ return nullptr;
+ }
+
+ return glTex;
+}
+
+static void uploadGLData(QOpenGLTexture *glTex,
+ int level, int layer, QOpenGLTexture::CubeMapFace face,
+ const QByteArray &bytes, const QTextureImageDataPtr &data)
+{
+ if (data->isCompressed()) {
+ glTex->setCompressedData(level, layer, face, bytes.size(), bytes.constData());
+ } else {
+ QOpenGLPixelTransferOptions uploadOptions;
+ uploadOptions.setAlignment(1);
+ glTex->setData(level, layer, face, data->pixelFormat(), data->pixelType(), bytes.constData(), &uploadOptions);
+ }
+}
+
+void GLTexture::uploadGLTextureData()
+{
+ // 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 = m_properties.generateMipMaps ? 1 : data->mipLevels();
+
+ for (int layer = 0; layer < data->layers(); layer++) {
+ for (int face = 0; face < data->faces(); face++) {
+ for (int level = 0; level < mipLevels; level++) {
+ // ensure we don't accidentally cause a detach / copy of the raw bytes
+ const QByteArray bytes(data->data(layer, face, level));
+ uploadGLData(m_gl, level, layer,
+ static_cast<QOpenGLTexture::CubeMapFace>(QOpenGLTexture::CubeMapPositiveX + face),
+ bytes, data);
+ }
+ }
+ }
+ }
+ }
+
+ // Upload all QTexImageData references by the TextureImages
+ for (int i = 0; i < m_images.size(); i++) {
+ const QTextureImageDataPtr &imgData = m_imageData.at(i);
+
+ // ensure we don't accidentally cause a detach / copy of the raw bytes
+ const QByteArray bytes(imgData->data());
+ uploadGLData(m_gl, m_images[i].mipLevel, m_images[i].layer,
+ static_cast<QOpenGLTexture::CubeMapFace>(m_images[i].face),
+ bytes, imgData);
+ }
+}
+
+void GLTexture::updateGLTextureParameters()
+{
+ m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX));
+ if (m_actualTarget != QAbstractTexture::Target1D &&
+ m_actualTarget != QAbstractTexture::Target1DArray &&
+ m_actualTarget != QAbstractTexture::TargetBuffer)
+ m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY));
+ if (m_actualTarget == QAbstractTexture::Target3D)
+ m_gl->setWrapMode(QOpenGLTexture::DirectionR, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ));
+ m_gl->setMinMagFilters(static_cast<QOpenGLTexture::Filter>(m_parameters.minificationFilter),
+ static_cast<QOpenGLTexture::Filter>(m_parameters.magnificationFilter));
+ if (m_gl->hasFeature(QOpenGLTexture::AnisotropicFiltering))
+ m_gl->setMaximumAnisotropy(m_parameters.maximumAnisotropy);
+ if (m_gl->hasFeature(QOpenGLTexture::TextureComparisonOperators)) {
+ m_gl->setComparisonFunction(static_cast<QOpenGLTexture::ComparisonFunction>(m_parameters.comparisonFunction));
+ m_gl->setComparisonMode(static_cast<QOpenGLTexture::ComparisonMode>(m_parameters.comparisonMode));
+ }
+}
+
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/textures/gltexture_p.h b/src/render/renderers/opengl/textures/gltexture_p.h
new file mode 100644
index 000000000..cde0a6973
--- /dev/null
+++ b/src/render/renderers/opengl/textures/gltexture_p.h
@@ -0,0 +1,245 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GLTEXTURE_H
+#define QT3DRENDER_RENDER_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 <QOpenGLContext>
+#include <QFlags>
+#include <QMutex>
+#include <QSize>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLTexture;
+
+namespace Qt3DRender {
+namespace Render {
+
+class TextureImageManager;
+class TextureDataManager;
+class TextureImageDataManager;
+class RenderBuffer;
+
+/**
+ * @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 GLTexture.
+ *
+ * Can be shared among multiple QTexture backend nodes through the
+ * GLTextureManager, which will make sure that there are no two GLTextures
+ * sharing the same texture data.
+ *
+ * A GLTexture can be unique though. In that case, it will not be shared
+ * between QTextures, but private to one QTexture only.
+ *
+ * A GLTexture 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 GLTexture will have no texture object under the hood, and therefore
+ * the only valid operation is getOrCreateRenderBuffer().
+ */
+class Q_AUTOTEST_EXPORT GLTexture
+{
+public:
+ GLTexture(TextureDataManager *texDataMgr,
+ TextureImageDataManager *texImgDataMgr,
+ const QTextureGeneratorPtr &texGen,
+ bool unique);
+
+ ~GLTexture();
+
+ /**
+ * 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 bool isUnique() const { return m_unique; }
+
+ inline TextureProperties properties() const { return m_properties; }
+ inline TextureParameters parameters() const { return m_parameters; }
+ inline QTextureGeneratorPtr textureGenerator() const { return m_dataFunctor; }
+ inline QVector<Image> images() const { return m_images; }
+
+ inline QSize size() const { return QSize(m_properties.width, m_properties.height); }
+ inline QOpenGLTexture *getGLTexture() const { return m_gl; }
+
+ /**
+ * @brief
+ * Returns the QOpenGLTexture for this GLTexture. 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.
+ */
+ QOpenGLTexture* getOrCreateGLTexture();
+
+ /**
+ * @brief
+ * Returns the RenderBuffer for this GLTexture. If this is the first
+ * call, the OpenGL renderbuffer object will be created.
+ */
+ RenderBuffer *getOrCreateRenderBuffer();
+
+ /**
+ * @brief Make sure to call this before calling the dtor
+ */
+ void destroyGLTexture();
+
+ // Called by TextureDataManager when it has new texture data from
+ // a generator that needs to be uploaded.
+ void requestUpload()
+ {
+ setDirtyFlag(TextureData, true);
+ }
+
+ bool isDirty()
+ {
+ return m_dirtyFlags.load() != 0;
+ }
+
+ QMutex *textureLock()
+ {
+ return &m_textureMutex;
+ }
+
+protected:
+ template<class APITexture, class APITextureImage>
+ friend class APITextureManager;
+
+ /*
+ * These methods are to be accessed from the GLTextureManager.
+ * The renderer and the texture backend nodes can only modify Textures
+ * through the GLTextureManager.
+ *
+ * The methods should only be called for unique textures, or textures
+ * that are not shared between multiple nodes.
+ */
+ void setParameters(const TextureParameters &params);
+ void setProperties(const TextureProperties &props);
+ void setImages(const QVector<Image> &images);
+ void setGenerator(const QTextureGeneratorPtr &generator);
+
+private:
+
+ enum DirtyFlag {
+ TextureData = 0x01, // one or more image generators have been executed, data needs uploading to GPU
+ Properties = 0x02, // texture needs to be (re-)created
+ Parameters = 0x04 // texture parameters need to be (re-)set
+
+ };
+
+ bool testDirtyFlag(DirtyFlag flag)
+ {
+ return m_dirtyFlags.load() & flag;
+ }
+
+ void setDirtyFlag(DirtyFlag flag, bool value = true)
+ {
+ if (value)
+ m_dirtyFlags |= flag;
+ else
+ m_dirtyFlags &= ~static_cast<int>(flag);
+ }
+
+ QOpenGLTexture *buildGLTexture();
+ void uploadGLTextureData();
+ void updateGLTextureParameters();
+ void destroyResources();
+
+ bool m_unique;
+ QAtomicInt m_dirtyFlags;
+ QMutex m_textureMutex;
+ QOpenGLTexture *m_gl;
+ RenderBuffer *m_renderBuffer;
+
+ TextureDataManager *m_textureDataManager;
+ TextureImageDataManager *m_textureImageDataManager;
+
+ // target which is actually used for GL texture
+ QAbstractTexture::Target m_actualTarget;
+ TextureProperties m_properties;
+ TextureParameters m_parameters;
+
+ QTextureGeneratorPtr m_dataFunctor;
+ QVector<Image> m_images;
+
+ // cache actual image data generated by the functors
+ QTextureDataPtr m_textureData;
+ QVector<QTextureImageDataPtr> m_imageData;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GLTEXTURE_H
diff --git a/src/render/renderers/opengl/textures/gltexturemanager_p.h b/src/render/renderers/opengl/textures/gltexturemanager_p.h
new file mode 100644
index 000000000..1c8b49911
--- /dev/null
+++ b/src/render/renderers/opengl/textures/gltexturemanager_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GLTEXTUREMANAGER_H
+#define QT3DRENDER_RENDER_GLTEXTUREMANAGER_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/apitexturemanager_p.h>
+#include <Qt3DRender/private/gltexture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class Q_AUTOTEST_EXPORT GLTextureManager : public APITextureManager<GLTexture, GLTexture::Image>
+{
+public:
+ explicit GLTextureManager(TextureImageManager *textureImageManager,
+ TextureDataManager *textureDataManager,
+ TextureImageDataManager *textureImageDataManager)
+ : APITextureManager<GLTexture, GLTexture::Image>(textureImageManager,
+ textureDataManager,
+ textureImageDataManager)
+ {}
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GLTEXTUREMANAGER_H
diff --git a/src/render/renderers/opengl/textures/renderbuffer.cpp b/src/render/renderers/opengl/textures/renderbuffer.cpp
new file mode 100644
index 000000000..bc5050f73
--- /dev/null
+++ b/src/render/renderers/opengl/textures/renderbuffer.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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 {
+
+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 Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/renderers/opengl/textures/renderbuffer_p.h b/src/render/renderers/opengl/textures/renderbuffer_p.h
new file mode 100644
index 000000000..7dc62492a
--- /dev/null
+++ b/src/render/renderers/opengl/textures/renderbuffer_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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_RENDERBUFFER_P_H
+#define QT3DRENDER_RENDER_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 {
+
+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 Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERBUFFER_P_H
diff --git a/src/render/renderers/opengl/textures/textures.pri b/src/render/renderers/opengl/textures/textures.pri
new file mode 100644
index 000000000..42ffff7eb
--- /dev/null
+++ b/src/render/renderers/opengl/textures/textures.pri
@@ -0,0 +1,12 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/gltexture.cpp \
+ $$PWD/renderbuffer.cpp
+
+HEADERS += \
+ $$PWD/gltexturemanager_p.h \
+ $$PWD/gltexture_p.h \
+ $$PWD/renderbuffer_p.h
+
+
diff --git a/src/render/renderers/renderers.pri b/src/render/renderers/renderers.pri
new file mode 100644
index 000000000..795a6fbdf
--- /dev/null
+++ b/src/render/renderers/renderers.pri
@@ -0,0 +1,7 @@
+TEMPLATE = subdirs
+
+# OpenGL renderer
+include($$OUT_PWD/qt3drender-config.pri)
+QT_FOR_CONFIG += 3drender-private
+
+qtConfig(qt3d-opengl-renderer): include(opengl/opengl.pri)