diff options
Diffstat (limited to 'src/plugins')
84 files changed, 24812 insertions, 0 deletions
diff --git a/src/plugins/renderers/dummy/dummy.pro b/src/plugins/renderers/dummy/dummy.pro new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/plugins/renderers/dummy/dummy.pro diff --git a/src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp new file mode 100644 index 000000000..fe989ff70 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp @@ -0,0 +1,1024 @@ +/**************************************************************************** +** +** 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/buffer_p.h> +#include <Qt3DRender/private/attribute_p.h> +#include <Qt3DRender/private/rendertarget_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/qbuffer_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/qbuffer_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <QOpenGLShaderProgram> +#include <glresourcemanagers_p.h> +#include <graphicshelperinterface_p.h> +#include <gltexture_p.h> +#include <rendercommand_p.h> +#include <renderer_p.h> +#include <renderbuffer_p.h> +#include <glshader_p.h> + +#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 <graphicshelpergl2_p.h> +#include <graphicshelpergl3_2_p.h> +#include <graphicshelpergl3_3_p.h> +#include <graphicshelpergl4_p.h> +#endif +#include <graphicshelperes2_p.h> +#include <graphicshelperes3_p.h> +#include <graphicshelperes3_1_p.h> +#include <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 + +#ifndef GL_MAX_IMAGE_UNITS +#define GL_MAX_IMAGE_UNITS 0x8F38 +#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_maxImageUnits(0) + , m_defaultFBO(0) + , m_gl(nullptr) + , m_glHelper(nullptr) + , m_debugLogger(nullptr) + , m_currentVAO(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_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &m_maxTextureUnits); + qCDebug(Backend) << "context supports" << m_maxTextureUnits << "texture units"; + m_gl->functions()->glGetIntegerv(GL_MAX_IMAGE_UNITS, &m_maxImageUnits); + qCDebug(Backend) << "context supports" << m_maxImageUnits << "image 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; + } + + initializeHelpers(surface); + + return true; +} + +void GraphicsContext::initializeHelpers(QSurface *surface) +{ + // 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); + } +} + +void GraphicsContext::doneCurrent() +{ + Q_ASSERT(m_gl); + m_gl->doneCurrent(); + m_glHelper = nullptr; +} + +// Called by GL Command Thread +GraphicsContext::ShaderCreationInfo GraphicsContext::createShaderProgram(GLShader *shader) +{ + QOpenGLShaderProgram *shaderProgram = shader->shaderProgram(); + + // Compile shaders + const auto shaderCode = shader->shaderCode(); + + QString logs; + for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) { + const QShaderProgram::ShaderType type = static_cast<QShaderProgram::ShaderType>(i); + if (!shaderCode.at(i).isEmpty()) { + // Note: logs only return the error but not all the shader code + // we could append it + 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(), shader->fragOutputs()); + + const bool linkSucceeded = shaderProgram->link(); + logs += shaderProgram->log(); + + // Perform shader introspection + introspectShaderInterface(shader); + + return {linkSucceeded, logs}; +} + +// That assumes that the shaderProgram in Shader stays the same +void GraphicsContext::introspectShaderInterface(GLShader *shader) +{ + QOpenGLShaderProgram *shaderProgram = shader->shaderProgram(); + GraphicsHelperInterface *glHelper = resolveHighestOpenGLFunctions(); + shader->initializeUniforms(glHelper->programUniformsAndLocations(shaderProgram->programId())); + shader->initializeAttributes(glHelper->programAttributesAndLocations(shaderProgram->programId())); + if (m_glHelper->supportsFeature(GraphicsHelperInterface::UniformBufferObject)) + shader->initializeUniformBlocks(m_glHelper->programUniformBlocks(shaderProgram->programId())); + if (m_glHelper->supportsFeature(GraphicsHelperInterface::ShaderStorageObject)) + shader->initializeShaderStorageBlocks(m_glHelper->programShaderStorageBlocks(shaderProgram->programId())); +} + + +// Called by Renderer::updateGLResources +void GraphicsContext::loadShader(Shader *shaderNode, + ShaderManager *shaderManager, + GLShaderManager *glShaderManager) +{ + const Qt3DCore::QNodeId shaderId = shaderNode->peerId(); + GLShader *glShader = glShaderManager->lookupResource(shaderId); + + // We already have a shader associated with the node + if (glShader != nullptr) { + // We need to abandon it + glShaderManager->abandon(glShader, shaderNode); + } + + // We create or adopt an already created glShader + glShader = glShaderManager->createOrAdoptExisting(shaderNode); + + const QVector<Qt3DCore::QNodeId> sharedShaderIds = glShaderManager->shaderIdsForProgram(glShader); + if (sharedShaderIds.size() == 1) { + // Shader in the cache hasn't been loaded yet + glShader->setGraphicsContext(this); + glShader->setShaderCode(shaderNode->shaderCode()); + const ShaderCreationInfo loadResult = createShaderProgram(glShader); + shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error); + shaderNode->setLog(loadResult.logs); + // Loaded in the sense we tried to load it (and maybe it failed) + glShader->setLoaded(true); + } else { + // Find an already loaded shader that shares the same QOpenGLShaderProgram + for (const Qt3DCore::QNodeId sharedShaderId : sharedShaderIds) { + if (sharedShaderId != shaderNode->peerId()) { + Shader *refShader = shaderManager->lookupResource(sharedShaderId); + // We only introspect once per actual OpenGL shader program + // rather than once per ShaderNode. + shaderNode->initializeFromReference(*refShader); + break; + } + } + } + shaderNode->unsetDirty(); + // Ensure we will rebuilt material caches + shaderNode->markDirty(AbstractRenderer::AllDirty); +} + +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"; + } +} + +void GraphicsContext::rasterMode(GLenum faceMode, GLenum rasterMode) +{ + m_glHelper->rasterMode(faceMode, rasterMode); +} + +/*! + * \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 if (m_gl->format().minorVersion() >= 1) { + glHelper = new GraphicsHelperES3_1; + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL ES 3.1 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::depthRange(GLdouble nearValue, GLdouble farValue) +{ + m_glHelper->depthRange(nearValue, farValue); +} + +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::bindImageTexture(GLuint imageUnit, GLuint texture, + GLint mipLevel, GLboolean layered, + GLint layer, GLenum access, GLenum format) +{ + m_glHelper->bindImageTexture(imageUnit, + texture, + mipLevel, + layered, + layer, + access, + format); +} + +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() const +{ + return m_maxTextureUnits; +} + +GLint GraphicsContext::maxImageUnitsCount() const +{ + return m_maxImageUnits; +} + + +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::drawBuffers(GLsizei n, const int *bufs) +{ + m_glHelper->drawBuffers(n, bufs); +} + +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::Image: + 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/plugins/renderers/opengl/graphicshelpers/graphicscontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicscontext_p.h new file mode 100644 index 000000000..07df3e9e4 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicscontext_p.h @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** 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/qclearbuffers.h> +#include <Qt3DRender/private/shader_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/uniform_p.h> +#include <Qt3DRender/private/qblitframebuffer_p.h> +#include <gl_handle_types_p.h> +#include <glbuffer_p.h> +#include <shaderparameterpack_p.h> +#include <graphicshelperinterface_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; +class GLShader; +class GLShaderManager; + +typedef QPair<QString, int> NamedUniformLocation; + +class Q_AUTOTEST_EXPORT GraphicsContext +{ +public: + GraphicsContext(); + ~GraphicsContext(); + + void setOpenGLContext(QOpenGLContext* ctx); + QOpenGLContext *openGLContext() { return m_gl; } + bool makeCurrent(QSurface *surface); + void doneCurrent(); + bool hasValidGLHelper() const; + bool isInitialized() const; + + // Shaders + struct ShaderCreationInfo + { + bool linkSucceeded = false; + QString logs; + }; + + ShaderCreationInfo createShaderProgram(GLShader *shaderNode); + void introspectShaderInterface(GLShader *shader); + void loadShader(Shader* shader, ShaderManager *shaderManager, GLShaderManager *glShaderManager); + + 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 bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format); + 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 depthRange(GLdouble nearValue, GLdouble farValue); + 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() const; + GLint maxImageUnitsCount() const; + void pointSize(bool programmable, GLfloat value); + void readBuffer(GLenum mode); + void drawBuffer(GLenum mode); + void drawBuffers(GLsizei n, const int *bufs); + 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); + void rasterMode(GLenum faceMode, GLenum rasterMode); + + // 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(); + void initializeHelpers(QSurface *surface); + GraphicsHelperInterface *resolveHighestOpenGLFunctions(); + + bool m_initialized; + bool m_supportsVAO; + GLint m_maxTextureUnits; + GLint m_maxImageUnits; + GLuint m_defaultFBO; + QOpenGLContext *m_gl; + GraphicsHelperInterface *m_glHelper; + + QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers; + GraphicsApiFilterData m_contextInfo; + 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/plugins/renderers/opengl/graphicshelpers/graphicshelperes2.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes2.cpp new file mode 100644 index 000000000..ba00325b1 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes2.cpp @@ -0,0 +1,1034 @@ +/**************************************************************************** +** +** 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 <qgraphicsutils_p.h> +#include <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 + +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 *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "Indirect Drawing is not supported with OpenGL ES 2"; +} + +void GraphicsHelperES2::drawArraysIndirect(GLenum , void *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "Indirect Drawing is not supported with OpenGL ES 2"; +} + +void GraphicsHelperES2::setVerticesPerPatch(GLint verticesPerPatch) +{ + Q_UNUSED(verticesPerPatch); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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; + static bool showWarning = true; + if (!showWarning) + return blocks; + showWarning = false; + 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; + static bool showWarning = true; + if (!showWarning) + return blocks; + showWarning = false; + 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) + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glReadBuffer not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; +} + +void GraphicsHelperES2::drawBuffer(GLenum mode) +{ + Q_UNUSED(mode); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glDrawBuffer is not supported with OpenGL ES 2"; +} + +void *GraphicsHelperES2::fenceSync() +{ + qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; + return nullptr; +} + +void GraphicsHelperES2::clientWaitSync(void *, GLuint64 ) +{ + qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; +} + +void GraphicsHelperES2::waitSync(void *) +{ + qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; +} + +bool GraphicsHelperES2::wasSyncSignaled(void *) +{ + qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; + return false; +} + +void GraphicsHelperES2::deleteSync(void *) +{ + qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; +} + +void GraphicsHelperES2::rasterMode(GLenum faceMode, GLenum rasterMode) +{ + Q_UNUSED(faceMode); + Q_UNUSED(rasterMode); + qWarning() << "glPolyonMode is not supported with OpenGL ES"; +} + +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); + + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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::depthRange(GLdouble nearValue, GLdouble farValue) +{ + m_funcs->glDepthRangef(static_cast<float>(nearValue), static_cast<float>(farValue)); +} + +void GraphicsHelperES2::frontFace(GLenum mode) +{ + m_funcs->glFrontFace(mode); +} + +void GraphicsHelperES2::setMSAAEnabled(bool enabled) +{ + Q_UNUSED(enabled); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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) +{ + Q_UNUSED(mode) + // For ES2 the spec states for target: The symbolic constant must be GL_FRAMEBUFFER + // so mode is ignored and is always set to GL_FRAMEBUFFER + m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId); +} + +void GraphicsHelperES2::bindImageTexture(GLuint imageUnit, GLuint texture, + GLint mipLevel, GLboolean layered, + GLint layer, GLenum access, GLenum format) +{ + Q_UNUSED(imageUnit) + Q_UNUSED(texture) + Q_UNUSED(mipLevel) + Q_UNUSED(layered) + Q_UNUSED(layer) + Q_UNUSED(access) + Q_UNUSED(format) + qWarning() << "Shader Images are not supported by ES 2.0 (since ES 3.1)"; + +} + +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 depth or stencil attachments since this is + // problematic before GLES 3.2. Keep using textures for everything else. + // For ES2 individual Depth and Stencil buffers need to be an option because + // DepthStencil is an extension. + return attachment.m_point == QRenderTargetOutput::DepthStencil || + attachment.m_point == QRenderTargetOutput::Depth || + attachment.m_point == QRenderTargetOutput::Stencil; +} + +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 && + attachment.m_point != QRenderTargetOutput::Depth && + attachment.m_point != QRenderTargetOutput::Stencil) { + qCritical() << "Renderbuffers only supported for combined depth-stencil, depth, or stencil, but got attachment point" + << attachment.m_point; + return; + } + + renderBuffer->bind(); + if (attachment.m_point == QRenderTargetOutput::DepthStencil || + attachment.m_point == QRenderTargetOutput::Depth) + m_funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->renderBufferId()); + if (attachment.m_point == QRenderTargetOutput::DepthStencil || + attachment.m_point == QRenderTargetOutput::Stencil) + 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 *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "Clip planes not supported by OpenGL ES 2.0"; +} + +GLint GraphicsHelperES2::maxClipPlaneCount() +{ + return 0; +} + +void GraphicsHelperES2::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + Q_UNUSED(barriers); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glEnablei() not supported by OpenGL ES 2.0"; +} + +void GraphicsHelperES2::disablei(GLenum cap, GLuint index) +{ + Q_UNUSED(cap); + Q_UNUSED(index); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glDisablei() not supported by OpenGL ES 2.0"; +} + +void GraphicsHelperES2::setSeamlessCubemap(bool enable) +{ + Q_UNUSED(enable); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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); + static bool showWarning = true; + if (!showWarning) + return nullptr; + showWarning = false; + qWarning() << "Map buffer is not a core requirement for ES 2.0"; + return nullptr; +} + +GLboolean GraphicsHelperES2::unmapBuffer(GLenum target) +{ + Q_UNUSED(target); + static bool showWarning = true; + if (!showWarning) + return false; + showWarning = false; + 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 *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniform1uiv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniform2uiv(GLint , GLsizei , const GLuint *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniform2uiv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniform3uiv(GLint , GLsizei , const GLuint *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniform3uiv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniform4uiv(GLint , GLsizei , const GLuint *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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 *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniformMatrix2x3fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix3x2fv(GLint , GLsizei , const GLfloat *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniformMatrix3x2fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix2x4fv(GLint , GLsizei , const GLfloat *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniformMatrix2x4fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix4x2fv(GLint , GLsizei , const GLfloat *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniformMatrix4x2fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix3x4fv(GLint , GLsizei , const GLfloat *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + qWarning() << "glUniformMatrix3x4fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix4x3fv(GLint , GLsizei , const GLfloat *) +{ + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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) { + static bool showWarning = true; + if (!showWarning) + return; + showWarning = false; + 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/plugins/renderers/opengl/graphicshelpers/graphicshelperes2_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes2_p.h new file mode 100644 index 000000000..902bd5fb2 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes2_p.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** 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 <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 bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format) 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 depthRange(GLdouble nearValue, GLdouble farValue) 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 rasterMode(GLenum faceMode, GLenum rasterMode) override; + + void *fenceSync() override; + void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override; + void waitSync(void *sync) override; + bool wasSyncSignaled(void *sync) override; + void deleteSync(void *sync) 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/plugins/renderers/opengl/graphicshelpers/graphicshelperes3.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3.cpp new file mode 100644 index 000000000..6e299199f --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3.cpp @@ -0,0 +1,768 @@ +/**************************************************************************** +** +** 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 <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 + +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif + +#ifndef GL_SIGNALED +#define GL_SIGNALED 0x9119 +#endif + +#ifndef GL_SYNC_STATUS +#define GL_SYNC_STATUS 0x9114 +#endif + +#ifndef GL_TIMEOUT_IGNORED +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +#ifndef GL_SYNC_GPU_COMMANDS_COMPLETE +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#endif + +#ifndef GL_SYNC_FLUSH_COMMANDS_BIT +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#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::drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) +{ + m_extraFuncs->glDrawArraysInstanced(primitiveType, + first, + count, + instances); +} + +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(); +} + +void GraphicsHelperES3::bindFrameBufferObject(GLuint frameBufferId, GraphicsHelperInterface::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; + } +} + +bool GraphicsHelperES3::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case RenderBufferDimensionRetrieval: + case MRT: + case BlitFramebuffer: + case UniformBufferObject: + case MapBuffer: + case Fences: + 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); + } +} + +uint GraphicsHelperES3::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_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: + rawByteSize = 4; + break; + } + + return arrayStride ? rawByteSize * arrayStride : rawByteSize; +} + +void *GraphicsHelperES3::fenceSync() +{ + return m_extraFuncs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +void GraphicsHelperES3::clientWaitSync(void *sync, GLuint64 nanoSecTimeout) +{ + m_extraFuncs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout); +} + +void GraphicsHelperES3::waitSync(void *sync) +{ + m_extraFuncs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED); +} + +bool GraphicsHelperES3::wasSyncSignaled(void *sync) +{ + GLint v; + m_extraFuncs->glGetSynciv(static_cast<GLsync>(sync), + GL_SYNC_STATUS, + sizeof(v), + nullptr, + &v); + return v == GL_SIGNALED; +} + +void GraphicsHelperES3::deleteSync(void *sync) +{ + m_extraFuncs->glDeleteSync(static_cast<GLsync>(sync)); +} + +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); +} + +bool GraphicsHelperES3::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 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/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_1.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_1.cpp new file mode 100644 index 000000000..dc042b0c6 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_1.cpp @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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_1_p.h" +#include <qgraphicsutils_p.h> +#include <QOpenGLExtraFunctions> + +QT_BEGIN_NAMESPACE + +// ES 3.1+ +#ifndef GL_SAMPLER_2D_MULTISAMPLE +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#endif +#ifndef GL_INT_SAMPLER_2D_MULTISAMPLE +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#endif +#ifndef GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#endif +#ifndef GL_ACTIVE_RESOURCES +#define GL_ACTIVE_RESOURCES 0x92F5 +#endif +#ifndef GL_BUFFER_BINDING +#define GL_BUFFER_BINDING 0x9302 +#endif +#ifndef GL_BUFFER_DATA_SIZE +#define GL_BUFFER_DATA_SIZE 0x9303 +#endif +#ifndef GL_NUM_ACTIVE_VARIABLES +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#endif +#ifndef GL_SHADER_STORAGE_BLOCK +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#endif +#ifndef GL_ALL_BARRIER_BITS +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#endif +#ifndef GL_ELEMENT_ARRAY_BARRIER_BIT +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM_BARRIER_BIT +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#endif +#ifndef GL_TEXTURE_FETCH_BARRIER_BIT +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#endif +#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#endif +#ifndef GL_COMMAND_BARRIER_BIT +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#endif +#ifndef GL_PIXEL_BUFFER_BARRIER_BIT +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#endif +#ifndef GL_TEXTURE_UPDATE_BARRIER_BIT +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#endif +#ifndef GL_BUFFER_UPDATE_BARRIER_BIT +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#endif +#ifndef GL_FRAMEBUFFER_BARRIER_BIT +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#endif +#ifndef GL_TRANSFORM_FEEDBACK_BARRIER_BIT +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#endif +#ifndef GL_ATOMIC_COUNTER_BARRIER_BIT +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#endif +#ifndef GL_SHADER_STORAGE_BARRIER_BIT +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#endif +#ifndef GL_IMAGE_2D +#define GL_IMAGE_2D 0x904D +#endif +#ifndef GL_IMAGE_3D +#define GL_IMAGE_3D 0x904E +#endif +#ifndef GL_IMAGE_CUBE +#define GL_IMAGE_CUBE 0x9050 +#endif +#ifndef GL_IMAGE_2D_ARRAY +#define GL_IMAGE_2D_ARRAY 0x9053 +#endif +#ifndef GL_INT_IMAGE_2D +#define GL_INT_IMAGE_2D 0x9058 +#endif +#ifndef GL_INT_IMAGE_3D +#define GL_INT_IMAGE_3D 0x9059 +#endif +#ifndef GL_INT_IMAGE_CUBE +#define GL_INT_IMAGE_CUBE 0x905B +#endif +#ifndef GL_INT_IMAGE_2D_ARRAY +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#endif +#ifndef GL_UNSIGNED_INT_IMAGE_2D +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#endif +#ifndef GL_UNSIGNED_INT_IMAGE_3D +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#endif +#ifndef GL_UNSIGNED_INT_IMAGE_CUBE +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#endif +#ifndef GL_UNSIGNED_INT_IMAGE_2D_ARRAY +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#endif + + +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)) + qWarning() << "QueryBuffer barrier not supported by ES 3.1"; + + return bits; +} + +} // anonymous + + +GraphicsHelperES3_1::GraphicsHelperES3_1() +{ +} + +GraphicsHelperES3_1::~GraphicsHelperES3_1() +{ +} + +bool GraphicsHelperES3_1::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case GraphicsHelperInterface::Compute: + case GraphicsHelperInterface::ShaderStorageObject: + case GraphicsHelperInterface::IndirectDrawing: + case GraphicsHelperInterface::ShaderImage: + return true; + default: + break; + } + return GraphicsHelperES3::supportsFeature(feature); +} + +void GraphicsHelperES3_1::bindImageTexture(GLuint imageUnit, GLuint texture, + GLint mipLevel, GLboolean layered, + GLint layer, GLenum access, GLenum format) +{ + m_extraFuncs->glBindImageTexture(imageUnit, + texture, + mipLevel, + layered, + layer, + access, + format); +} + +void GraphicsHelperES3_1::dispatchCompute(GLuint wx, GLuint wy, GLuint wz) +{ + m_extraFuncs->glDispatchCompute(wx, wy, wz); +} + +void GraphicsHelperES3_1::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + m_extraFuncs->glMemoryBarrier(memoryBarrierGLBitfield(barriers)); +} + +void GraphicsHelperES3_1::drawArraysIndirect(GLenum mode, void *indirect) +{ + m_extraFuncs->glDrawArraysIndirect(mode, indirect); +} + +void GraphicsHelperES3_1::drawElementsIndirect(GLenum mode, GLenum type, void *indirect) +{ + m_extraFuncs->glDrawElementsIndirect(mode, type, indirect); +} + +void GraphicsHelperES3_1::bindShaderStorageBlock(GLuint , GLuint , GLuint ) +{ + // ES 3.1 has no API for that, bindings have to be specified directly in the shader + // with layout(std430, binding = 3) + qWarning() << "ES 3.1 has no bindShaderStorageBlock API, it uses binding declaration from the shader storage block"; +} + +QVector<ShaderStorageBlock> GraphicsHelperES3_1::programShaderStorageBlocks(GLuint programId) +{ + QVector<ShaderStorageBlock> blocks; + GLint nbrActiveShaderStorageBlocks = 0; + m_extraFuncs->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_extraFuncs->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_extraFuncs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_binding); + prop = GL_BUFFER_DATA_SIZE; + m_extraFuncs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_size); + prop = GL_NUM_ACTIVE_VARIABLES; + m_extraFuncs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_activeVariablesCount); + blocks.push_back(storageBlock); + } + return blocks; +} + +UniformType GraphicsHelperES3_1::uniformTypeFromGLType(GLenum glType) +{ + switch (glType) { + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return UniformType::Sampler; + case GL_IMAGE_2D: + case GL_IMAGE_3D: + case GL_IMAGE_CUBE: + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + return UniformType::Image; + + default: + return GraphicsHelperES3::uniformTypeFromGLType(glType); + } +} + +uint GraphicsHelperES3_1::uniformByteSize(const ShaderUniform &description) +{ + uint rawByteSize = 0; + + switch (description.m_type) { + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_IMAGE_2D: + case GL_IMAGE_3D: + case GL_IMAGE_CUBE: + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + rawByteSize = 4; + break; + + default: + rawByteSize = GraphicsHelperES3::uniformByteSize(description); + break; + } + + return rawByteSize; +} + +void GraphicsHelperES3_1::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + char *bufferData = buffer.data(); + + switch (description.m_type) { + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + { + Q_ASSERT(description.m_size == 1); + int value = v.toInt(); + QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1); + break; + } + + default: + GraphicsHelperES3::buildUniformBuffer(v, description, buffer); + break; + } +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_1_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_1_p.h new file mode 100644 index 000000000..7eab61c60 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_1_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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_1_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERES3_1_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 <graphicshelperes3_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class GraphicsHelperES3_1 : public GraphicsHelperES3 +{ +public: + GraphicsHelperES3_1(); + ~GraphicsHelperES3_1(); + + bool supportsFeature(Feature feature) const override; + void bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format) override; + void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override; + void memoryBarrier(QMemoryBarrier::Operations barriers) override; + void drawArraysIndirect(GLenum mode,void *indirect) override; + void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override; + void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override; + QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override; + + // QGraphicHelperInterface interface + UniformType uniformTypeFromGLType(GLenum glType) override; + uint uniformByteSize(const ShaderUniform &description) override; + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES3_1_H diff --git a/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp new file mode 100644 index 000000000..9d0988410 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** 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 + +#ifndef GL_PATCH_VERTICES +#define GL_PATCH_VERTICES 36466 +#endif + +#ifndef GL_IMAGE_BUFFER +#define GL_IMAGE_BUFFER 0x9051 +#endif +#ifndef GL_IMAGE_CUBE_MAP_ARRAY +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#endif +#ifndef GL_INT_IMAGE_BUFFER +#define GL_INT_IMAGE_BUFFER 0x905C +#endif +#ifndef GL_INT_IMAGE_CUBE_MAP_ARRAY +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#endif +#ifndef GL_UNSIGNED_INT_IMAGE_BUFFER +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#endif +#ifndef GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#endif + +namespace Qt3DRender { +namespace Render { + +GraphicsHelperES3_2::GraphicsHelperES3_2() +{ +} + +GraphicsHelperES3_2::~GraphicsHelperES3_2() +{ +} + +bool GraphicsHelperES3_2::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case GraphicsHelperInterface::Tessellation: + return true; + default: + break; + } + return GraphicsHelperES3_1::supportsFeature(feature); +} + +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(); +} + +void GraphicsHelperES3_2::setVerticesPerPatch(GLint verticesPerPatch) +{ + m_extraFuncs->glPatchParameteri(GL_PATCH_VERTICES, verticesPerPatch); +} + +void GraphicsHelperES3_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"; + + m_extraFuncs->glDrawElementsInstancedBaseVertex(primitiveType, + primitiveCount, + indexType, + indices, + instances, + baseVertex); +} + +UniformType GraphicsHelperES3_2::uniformTypeFromGLType(GLenum glType) +{ + switch (glType) { + case GL_IMAGE_BUFFER: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: + return UniformType::Image; + + default: + return GraphicsHelperES3_1::uniformTypeFromGLType(glType); + } +} + +uint GraphicsHelperES3_2::uniformByteSize(const ShaderUniform &description) +{ + uint rawByteSize = 0; + + switch (description.m_type) { + case GL_IMAGE_BUFFER: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: + rawByteSize = 4; + break; + + default: + rawByteSize = GraphicsHelperES3_1::uniformByteSize(description); + break; + } + + return rawByteSize; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h new file mode 100644 index 000000000..9552e656a --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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 <graphicshelperes3_1_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class GraphicsHelperES3_2 : public GraphicsHelperES3_1 +{ +public: + GraphicsHelperES3_2(); + ~GraphicsHelperES3_2(); + + bool supportsFeature(Feature feature) const override; + + // QGraphicHelperInterface interface + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; + void setVerticesPerPatch(GLint verticesPerPatch) override; + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override; + UniformType uniformTypeFromGLType(GLenum glType) override; + uint uniformByteSize(const ShaderUniform &description) override; + +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES3_2_H diff --git a/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_p.h new file mode 100644 index 000000000..c985ba24a --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperes3_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 <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; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) 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 drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) 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; + uint uniformByteSize(const ShaderUniform &description) override; + + void *fenceSync() override; + void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override; + void waitSync(void *sync) override; + bool wasSyncSignaled(void *sync) override; + void deleteSync(void *sync) override; + +protected: + QOpenGLExtraFunctions *m_extraFuncs = nullptr; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES3_H diff --git a/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp new file mode 100644 index 000000000..3afc29d2f --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp @@ -0,0 +1,935 @@ +/**************************************************************************** +** +** 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 <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::fenceSync() +{ + qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)"; + return nullptr; +} + +void GraphicsHelperGL2::clientWaitSync(void *, GLuint64 ) +{ + qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)"; +} + +void GraphicsHelperGL2::waitSync(void *) +{ + qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)"; +} + +bool GraphicsHelperGL2::wasSyncSignaled(void *) +{ + qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)"; + return false; +} + +void GraphicsHelperGL2::deleteSync(void *) +{ + qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)"; +} + +void GraphicsHelperGL2::rasterMode(GLenum faceMode, GLenum rasterMode) +{ + m_funcs->glPolygonMode(faceMode, rasterMode); +} + +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::depthRange(GLdouble nearValue, GLdouble farValue) +{ + m_funcs->glDepthRange(nearValue, farValue); +} + +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: + case MapBuffer: + 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"; + } +} + +void GraphicsHelperGL2::bindImageTexture(GLuint imageUnit, GLuint texture, + GLint mipLevel, GLboolean layered, + GLint layer, GLenum access, GLenum format) +{ + Q_UNUSED(imageUnit) + Q_UNUSED(texture) + Q_UNUSED(mipLevel) + Q_UNUSED(layered) + Q_UNUSED(layer) + Q_UNUSED(access) + Q_UNUSED(format) + qWarning() << "Shader Images are not supported by OpenGL 2.0 (since OpenGL 4.2)"; + +} + +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/plugins/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h new file mode 100644 index 000000000..35391471f --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** 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 <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 bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format) 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 depthRange(GLdouble nearValue, GLdouble farValue) 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 rasterMode(GLenum faceMode, GLenum rasterMode) override; + + void *fenceSync() override; + void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override; + void waitSync(void *sync) override; + bool wasSyncSignaled(void *sync) override; + void deleteSync(void *sync) 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/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp new file mode 100644 index 000000000..17850cf00 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp @@ -0,0 +1,1245 @@ +/**************************************************************************** +** +** 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 <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::fenceSync() +{ + return m_funcs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +void GraphicsHelperGL3_2::clientWaitSync(void *sync, GLuint64 nanoSecTimeout) +{ + m_funcs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout); +} + +void GraphicsHelperGL3_2::waitSync(void *sync) +{ + m_funcs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED); +} + +bool GraphicsHelperGL3_2::wasSyncSignaled(void *sync) +{ + GLint v; + m_funcs->glGetSynciv(static_cast<GLsync>(sync), + GL_SYNC_STATUS, + sizeof(v), + nullptr, + &v); + return v == GL_SIGNALED; +} + +void GraphicsHelperGL3_2::deleteSync(void *sync) +{ + m_funcs->glDeleteSync(static_cast<GLsync>(sync)); +} + +void GraphicsHelperGL3_2::rasterMode(GLenum faceMode, GLenum rasterMode) +{ + m_funcs->glPolygonMode(faceMode, rasterMode); +} + +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::depthRange(GLdouble nearValue, GLdouble farValue) +{ + m_funcs->glDepthRange(nearValue, farValue); +} + +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; + } +} + +void GraphicsHelperGL3_2::bindImageTexture(GLuint imageUnit, GLuint texture, + GLint mipLevel, GLboolean layered, + GLint layer, GLenum access, GLenum format) +{ + Q_UNUSED(imageUnit) + Q_UNUSED(texture) + Q_UNUSED(mipLevel) + Q_UNUSED(layered) + Q_UNUSED(layer) + Q_UNUSED(access) + Q_UNUSED(format) + qWarning() << "Shader Images are not supported by OpenGL 3.2 (since OpenGL 4.2)"; + +} + +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: + case Fences: + 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/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h new file mode 100644 index 000000000..b6b2f7141 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** 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 <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 bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format) 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 depthRange(GLdouble nearValue, GLdouble farValue) 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 rasterMode(GLenum faceMode, GLenum rasterMode) override; + + void *fenceSync() override; + void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override; + void waitSync(void *sync) override; + bool wasSyncSignaled(void *sync) override; + void deleteSync(void *sync) 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/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp new file mode 100644 index 000000000..3d8e2acfc --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp @@ -0,0 +1,1240 @@ +/**************************************************************************** +** +** 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 <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::fenceSync() +{ + return m_funcs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +void GraphicsHelperGL3_3::clientWaitSync(void *sync, GLuint64 nanoSecTimeout) +{ + m_funcs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout); +} + +void GraphicsHelperGL3_3::waitSync(void *sync) +{ + m_funcs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED); +} + +bool GraphicsHelperGL3_3::wasSyncSignaled(void *sync) +{ + GLint v; + m_funcs->glGetSynciv(static_cast<GLsync>(sync), + GL_SYNC_STATUS, + sizeof(v), + nullptr, + &v); + return v == GL_SIGNALED; +} + +void GraphicsHelperGL3_3::deleteSync(void *sync) +{ + m_funcs->glDeleteSync(static_cast<GLsync>(sync)); +} + +void GraphicsHelperGL3_3::rasterMode(GLenum faceMode, GLenum rasterMode) +{ + m_funcs->glPolygonMode(faceMode, rasterMode); +} + +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::depthRange(GLdouble nearValue, GLdouble farValue) +{ + m_funcs->glDepthRange(nearValue, farValue); +} + +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: + case Fences: + 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::bindImageTexture(GLuint imageUnit, GLuint texture, + GLint mipLevel, GLboolean layered, + GLint layer, GLenum access, GLenum format) +{ + Q_UNUSED(imageUnit) + Q_UNUSED(texture) + Q_UNUSED(mipLevel) + Q_UNUSED(layered) + Q_UNUSED(layer) + Q_UNUSED(access) + Q_UNUSED(format) + qWarning() << "Shader Images are not supported by OpenGL 3.3 (since OpenGL 4.2)"; +} + +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/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h new file mode 100644 index 000000000..8d8525111 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** 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 <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 bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format) 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 depthRange(GLdouble nearValue, GLdouble farValue) 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 rasterMode(GLenum faceMode, GLenum rasterMode) override; + + void *fenceSync() override; + void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override; + void waitSync(void *sync) override; + bool wasSyncSignaled(void *sync) override; + void deleteSync(void *sync) 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/plugins/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp new file mode 100644 index 000000000..775dd7935 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp @@ -0,0 +1,1442 @@ +/**************************************************************************** +** +** 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 <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 +# define GL_IMAGE_1D 0x904C +# define GL_IMAGE_2D 0x904D +# define GL_IMAGE_3D 0x904E +# define GL_IMAGE_2D_RECT 0x904F +# define GL_IMAGE_CUBE 0x9050 +# define GL_IMAGE_BUFFER 0x9051 +# define GL_IMAGE_1D_ARRAY 0x9052 +# define GL_IMAGE_2D_ARRAY 0x9053 +# define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +# define GL_IMAGE_2D_MULTISAMPLE 0x9055 +# define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +# define GL_INT_IMAGE_1D 0x9057 +# define GL_INT_IMAGE_2D 0x9058 +# define GL_INT_IMAGE_3D 0x9059 +# define GL_INT_IMAGE_2D_RECT 0x905A +# define GL_INT_IMAGE_CUBE 0x905B +# define GL_INT_IMAGE_BUFFER 0x905C +# define GL_INT_IMAGE_1D_ARRAY 0x905D +# define GL_INT_IMAGE_2D_ARRAY 0x905E +# define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +# define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +# define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +# define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +# define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +# define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +# define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +# define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +# define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +# define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +# define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +# define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +# define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +# define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +# 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::fenceSync() +{ + return m_funcs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +void GraphicsHelperGL4::clientWaitSync(void *sync, GLuint64 nanoSecTimeout) +{ + qDebug() << Q_FUNC_INFO << sync << static_cast<GLsync>(sync); + GLenum e = m_funcs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout); + qDebug() << e; +} + +void GraphicsHelperGL4::waitSync(void *sync) +{ + m_funcs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED); +} + +bool GraphicsHelperGL4::wasSyncSignaled(void *sync) +{ + GLint v = 0; + m_funcs->glGetSynciv(static_cast<GLsync>(sync), + GL_SYNC_STATUS, + sizeof(v), + nullptr, + &v); + return v == GL_SIGNALED; +} + +void GraphicsHelperGL4::deleteSync(void *sync) +{ + m_funcs->glDeleteSync(static_cast<GLsync>(sync)); +} + +void GraphicsHelperGL4::rasterMode(GLenum faceMode, GLenum rasterMode) +{ + m_funcs->glPolygonMode(faceMode, rasterMode); +} + +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; + + case GL_IMAGE_1D: + case GL_IMAGE_2D: + case GL_IMAGE_3D: + case GL_IMAGE_2D_RECT: + case GL_IMAGE_CUBE: + case GL_IMAGE_BUFFER: + case GL_IMAGE_1D_ARRAY: + case GL_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_IMAGE_2D_MULTISAMPLE: + case GL_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_INT_IMAGE_1D: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_2D_RECT: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_1D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_2D_MULTISAMPLE: + case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_2D_RECT: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + return UniformType::Image; + + 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::depthRange(GLdouble nearValue, GLdouble farValue) +{ + m_funcs->glDepthRange(nearValue, farValue); +} + +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; + } +} + +void GraphicsHelperGL4::bindImageTexture(GLuint imageUnit, GLuint texture, + GLint mipLevel, GLboolean layered, + GLint layer, GLenum access, GLenum format) +{ + m_funcs->glBindImageTexture(imageUnit, + texture, + mipLevel, + layered, + layer, + access, + format); +} + +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: + case MapBuffer: + case Fences: + case ShaderImage: + 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: + case GL_IMAGE_1D: + case GL_IMAGE_2D: + case GL_IMAGE_3D: + case GL_IMAGE_2D_RECT: + case GL_IMAGE_CUBE: + case GL_IMAGE_BUFFER: + case GL_IMAGE_1D_ARRAY: + case GL_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_IMAGE_2D_MULTISAMPLE: + case GL_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_INT_IMAGE_1D: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_2D_RECT: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_1D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_2D_MULTISAMPLE: + case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_2D_RECT: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_IMAGE_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: + case GL_IMAGE_1D: + case GL_IMAGE_2D: + case GL_IMAGE_3D: + case GL_IMAGE_2D_RECT: + case GL_IMAGE_CUBE: + case GL_IMAGE_BUFFER: + case GL_IMAGE_1D_ARRAY: + case GL_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_IMAGE_2D_MULTISAMPLE: + case GL_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_INT_IMAGE_1D: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_2D_RECT: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_1D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_2D_MULTISAMPLE: + case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_2D_RECT: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + rawByteSize = 4; + break; + + default: { + qWarning() << Q_FUNC_INFO << "unable to deduce rawByteSize for uniform type:" << description.m_type << "for uniform" << description.m_name; + 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/plugins/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h new file mode 100644 index 000000000..02cb7e211 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** 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 <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 bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format) 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 depthRange(GLdouble nearValue, GLdouble farValue) 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 rasterMode(GLenum faceMode, GLenum rasterMode) override; + + void *fenceSync() override; + void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override; + void waitSync(void *sync) override; + bool wasSyncSignaled(void *sync) override; + void deleteSync(void *sync) 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/plugins/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h new file mode 100644 index 000000000..aa2b65c66 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** 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 <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, + Fences, + ShaderImage + }; + + 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 bindImageTexture(GLuint imageUnit, GLuint texture, GLint mipLevel, GLboolean layered, GLint layer, GLenum access, GLenum format) = 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 depthRange(GLdouble nearValue, GLdouble farValue) = 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 rasterMode(GLenum faceMode, GLenum rasterMode) = 0; + + virtual void *fenceSync() = 0; + virtual void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) = 0; + virtual void waitSync(void *sync) = 0; + virtual bool wasSyncSignaled(void *sync) = 0; + virtual void deleteSync(void *sync) = 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/plugins/renderers/opengl/graphicshelpers/graphicshelpers.pri b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpers.pri new file mode 100644 index 000000000..3cfa445ce --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicshelpers.pri @@ -0,0 +1,33 @@ +#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_1_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/imagesubmissioncontext_p.h \ + $$PWD/submissioncontext_p.h \ + $$PWD/texturesubmissioncontext_p.h \ + $$PWD/qgraphicsutils_p.h + +SOURCES += \ + $$PWD/graphicscontext.cpp \ + $$PWD/graphicshelperes2.cpp \ + $$PWD/graphicshelperes3.cpp \ + $$PWD/graphicshelperes3_1.cpp \ + $$PWD/graphicshelperes3_2.cpp \ + $$PWD/graphicshelpergl2.cpp \ + $$PWD/graphicshelpergl3_3.cpp \ + $$PWD/graphicshelpergl4.cpp \ + $$PWD/graphicshelpergl3_2.cpp \ + $$PWD/imagesubmissioncontext.cpp \ + $$PWD/submissioncontext.cpp \ + $$PWD/texturesubmissioncontext.cpp diff --git a/src/plugins/renderers/opengl/graphicshelpers/imagesubmissioncontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/imagesubmissioncontext.cpp new file mode 100644 index 000000000..cd2b33fb4 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/imagesubmissioncontext.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "imagesubmissioncontext_p.h" +#include <Qt3DRender/private/shaderimage_p.h> +#include <Qt3DRender/qshaderimage.h> +#include <graphicscontext_p.h> +#include <gltexture_p.h> + +QT_BEGIN_NAMESPACE + +// ES 3.1+ or GL 4.2+ +#ifndef GL_READ_ONLY +#define GL_READ_ONLY 0x88B8 +#endif +#ifndef GL_WRITE_ONLY +#define GL_WRITE_ONLY 0x88B9 +#endif +#ifndef GL_READ_WRITE +#define GL_READ_WRITE 0x88BA +#endif + +namespace Qt3DRender { +namespace Render { + +class GraphicsContext; +class GLTexture; + +namespace { + +GLenum glAccessEnumForShaderImageAccess(QShaderImage::Access access) +{ + switch (access) { + case QShaderImage::ReadOnly: + return GL_READ_ONLY; + case QShaderImage::WriteOnly: + return GL_WRITE_ONLY; + case QShaderImage::ReadWrite: + default: + break; + } + return GL_READ_WRITE; +} + +GLenum glImageFormatToGL(QShaderImage::ImageFormat format) +{ + // Right now we can abuse from the fact that the ImageFormat enum values + // have been assigned the same value as the GL enum + return GLenum(format); +} + +GLenum glImageFormatForShaderImageFormat(QShaderImage::ImageFormat format, + QAbstractTexture::TextureFormat textureFormat) +{ + Q_ASSERT_X(format != QShaderImage::NoFormat, Q_FUNC_INFO, "Valid image format or Automatic expected"); + + if (format != QShaderImage::Automatic) + return glImageFormatToGL(format); + + // Otherwise try to mind to best texture format + switch (textureFormat) { + case QAbstractTexture::R8_UNorm: + return glImageFormatToGL(QShaderImage::R8_UNorm); + case QAbstractTexture::RG8_UNorm: + return glImageFormatToGL(QShaderImage::RG8_UNorm); + case QAbstractTexture::RGBA8_UNorm: + return glImageFormatToGL(QShaderImage::RGBA8_UNorm); + + case QAbstractTexture::R16_UNorm: + return glImageFormatToGL(QShaderImage::R16_UNorm); + case QAbstractTexture::RG16_UNorm: + return glImageFormatToGL(QShaderImage::RG16_UNorm); + case QAbstractTexture::RGBA16_UNorm: + return glImageFormatToGL(QShaderImage::RGBA16_UNorm); + + case QAbstractTexture::R8_SNorm: + return glImageFormatToGL(QShaderImage::R8_SNorm); + case QAbstractTexture::RG8_SNorm: + return glImageFormatToGL(QShaderImage::RG8_SNorm); + case QAbstractTexture::RGBA8_SNorm: + return glImageFormatToGL(QShaderImage::RGBA8_SNorm); + + case QAbstractTexture::R16_SNorm: + return glImageFormatToGL(QShaderImage::R16_SNorm); + case QAbstractTexture::RG16_SNorm: + return glImageFormatToGL(QShaderImage::RG16_SNorm); + case QAbstractTexture::RGBA16_SNorm: + return glImageFormatToGL(QShaderImage::RGBA16_SNorm); + + case QAbstractTexture::R8U: + return glImageFormatToGL(QShaderImage::R8U); + case QAbstractTexture::RG8U: + return glImageFormatToGL(QShaderImage::RG8U); + case QAbstractTexture::RGBA8U: + return glImageFormatToGL(QShaderImage::RGBA8U); + + case QAbstractTexture::R16U: + return glImageFormatToGL(QShaderImage::R16U); + case QAbstractTexture::RG16U: + return glImageFormatToGL(QShaderImage::RG16U); + case QAbstractTexture::RGBA16U: + return glImageFormatToGL(QShaderImage::RGBA16U); + + case QAbstractTexture::R32U: + return glImageFormatToGL(QShaderImage::R32U); + case QAbstractTexture::RG32U: + return glImageFormatToGL(QShaderImage::RG32U); + case QAbstractTexture::RGBA32U: + return glImageFormatToGL(QShaderImage::RGBA32U); + + case QAbstractTexture::R8I: + return glImageFormatToGL(QShaderImage::R8I); + case QAbstractTexture::RG8I: + return glImageFormatToGL(QShaderImage::RG8I); + case QAbstractTexture::RGBA8I: + return glImageFormatToGL(QShaderImage::RGBA8I); + + case QAbstractTexture::R16I: + return glImageFormatToGL(QShaderImage::R16I); + case QAbstractTexture::RG16I: + return glImageFormatToGL(QShaderImage::RG16I); + case QAbstractTexture::RGBA16I: + return glImageFormatToGL(QShaderImage::RGBA16I); + + case QAbstractTexture::R32I: + return glImageFormatToGL(QShaderImage::R32I); + case QAbstractTexture::RG32I: + return glImageFormatToGL(QShaderImage::RG32I); + case QAbstractTexture::RGBA32I: + return glImageFormatToGL(QShaderImage::RGBA32I); + + case QAbstractTexture::R16F: + return glImageFormatToGL(QShaderImage::R16F); + case QAbstractTexture::RG16F: + return glImageFormatToGL(QShaderImage::RG16F); + case QAbstractTexture::RGBA16F: + return glImageFormatToGL(QShaderImage::RGBA16F); + + case QAbstractTexture::R32F: + return glImageFormatToGL(QShaderImage::R32F); + case QAbstractTexture::RG32F: + return glImageFormatToGL(QShaderImage::RG32F); + case QAbstractTexture::RGBA32F: + return glImageFormatToGL(QShaderImage::RGBA32F); + + case QAbstractTexture::RG11B10F: + return glImageFormatToGL(QShaderImage::RG11B10F); + case QAbstractTexture::RGB10A2: + return glImageFormatToGL(QShaderImage::RGB10A2); + case QAbstractTexture::RGB10A2U: + return glImageFormatToGL(QShaderImage::RGB10A2U); + + default: + qWarning() << "Cannot map Texture format" << textureFormat << "to a valid Image Format"; + Q_UNREACHABLE(); + return GL_NONE; + } +} + +} // anonymous + +ImageSubmissionContext::ImageSubmissionContext() + : m_ctx(nullptr) +{ +} + +void ImageSubmissionContext::initialize(GraphicsContext *context) +{ + m_ctx = context; + m_activeImages.resize(m_ctx->maxImageUnitsCount()); +} + +void ImageSubmissionContext::endDrawing() +{ + // Reduce score of all active Images + decayImageScores(); +} + +// Return Image Unit for Image +// If Image was used previously and recently, it will return the last used unit +// for that image. Otherwise it will try to return the image unit the least used. +int ImageSubmissionContext::activateImage(ShaderImage *image, GLTexture *tex) +{ + const int onUnit = assignUnitForImage(image->peerId()); + + if (onUnit < 0) { + qWarning() << "Unable to find available image unit"; + return -1; + } + + QOpenGLTexture *glTex = tex->getGLTexture(); + if (glTex == nullptr) { + qWarning() << "Unable to retrieve valid texture for Image"; + return -1; + } + + // Bind Image against Texture and resolve Image Format + m_ctx->bindImageTexture(onUnit, + glTex->textureId(), + image->mipLevel(), + image->layered(), + image->layer(), + glAccessEnumForShaderImageAccess(image->access()), + glImageFormatForShaderImageFormat(image->format(), + tex->properties().format)); + + // Store information about the Texture/Image on ActiveImage for given + // image unit + m_activeImages[onUnit].shaderImageId = image->peerId(); + m_activeImages[onUnit].texture = tex; + m_activeImages[onUnit].score = 200; + m_activeImages[onUnit].pinned = true; + + return onUnit; +} + +// Unset pinned Active Image and reduce their score +void ImageSubmissionContext::deactivateImages() +{ + for (int u = 0, m = m_activeImages.size(); u < m; ++u) { + if (m_activeImages[u].pinned) { + m_activeImages[u].pinned = false; + m_activeImages[u].score = qMax(m_activeImages[u].score - 1, 0); + return; + } + } +} + +// Reduce score of all active images (pinned or not) +void ImageSubmissionContext::decayImageScores() +{ + for (int u = 0, m = m_activeImages.size(); u < m; ++u) + m_activeImages[u].score = qMax(m_activeImages[u].score - 1, 0); +} + +int ImageSubmissionContext::assignUnitForImage(Qt3DCore::QNodeId shaderImageId) +{ + int lowestScoredUnit = -1; + int lowestScore = 0xfffffff; + + const int m = m_activeImages.size(); + for (int u = 0; u < m; ++u) { + if (m_activeImages[u].shaderImageId == shaderImageId) + return u; + } + + for (int u = 0; u < m; ++u) { + // No image is currently active on the image unit + // we save the image unit with the texture that has been on there + // the longest time while not being used + if (!m_activeImages[u].pinned) { + const int score = m_activeImages[u].score; + if (score < lowestScore) { + lowestScore = score; + lowestScoredUnit = u; + } + } + } + + if (lowestScoredUnit == -1) + qCWarning(Backend) << Q_FUNC_INFO << "No free image units!"; + + return lowestScoredUnit; +} + + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/graphicshelpers/imagesubmissioncontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/imagesubmissioncontext_p.h new file mode 100644 index 000000000..6d39f469b --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/imagesubmissioncontext_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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_IMAGESUBMISSIONCONTEXT_P_H +#define QT3DRENDER_RENDER_IMAGESUBMISSIONCONTEXT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/QNodeId> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class GraphicsContext; +class GLTexture; +class ShaderImage; + +class Q_AUTOTEST_EXPORT ImageSubmissionContext +{ +public: + ImageSubmissionContext(); + + void initialize(GraphicsContext *context); + void endDrawing(); + int activateImage(ShaderImage *image, GLTexture *tex); + void deactivateImages(); + +private: + void decayImageScores(); + int assignUnitForImage(Qt3DCore::QNodeId shaderImageId); + + struct ActiveImage + { + Qt3DCore::QNodeId shaderImageId; + GLTexture *texture = nullptr; + int score = 0; + bool pinned = false; + }; + QVector<ActiveImage> m_activeImages; + GraphicsContext *m_ctx; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_IMAGESUBMISSIONCONTEXT_P_H diff --git a/src/plugins/renderers/opengl/graphicshelpers/qgraphicsutils_p.h b/src/plugins/renderers/opengl/graphicshelpers/qgraphicsutils_p.h new file mode 100644 index 000000000..efc952ea5 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/qgraphicsutils_p.h @@ -0,0 +1,410 @@ +/**************************************************************************** +** +** 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_QGRAPHICSUTILS_P_H +#define QT3DRENDER_RENDER_QGRAPHICSUTILS_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 <QMatrix2x2> +#include <QMatrix3x3> +#include <QMatrix4x4> +#include <QGenericMatrix> +#include <QVector2D> +#include <QVarLengthArray> +#include <QColor> +#include <shadervariables_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +namespace { + +const int QMatrix2x2Type = qMetaTypeId<QMatrix2x2>(); +const int QMatrix2x3Type = qMetaTypeId<QMatrix2x3>(); +const int QMatrix2x4Type = qMetaTypeId<QMatrix2x4>(); +const int QMatrix3x2Type = qMetaTypeId<QMatrix3x2>(); +const int QMatrix3x3Type = qMetaTypeId<QMatrix3x3>(); +const int QMatrix3x4Type = qMetaTypeId<QMatrix3x4>(); +const int QMatrix4x2Type = qMetaTypeId<QMatrix4x2>(); +const int QMatrix4x3Type = qMetaTypeId<QMatrix4x3>(); + +} + +class QGraphicsUtils +{ + +public: + + template<typename T> + static const char *bytesFromVariant(const QVariant &v) + { + uint byteSize = sizeof(T); + // Max 16 float that we may want as doubles + // 64 should be best for most cases + static QVarLengthArray<char, 64> array(16 * byteSize); + memset(array.data(), 0, array.size()); + + switch (static_cast<QMetaType::Type>(v.type())) { + + // 1 byte + case QMetaType::Bool: { + T data = v.value<bool>(); + memcpy(array.data(), &data, byteSize); + break; + } + case QMetaType::Char: { + T data = v.value<char>(); + memcpy(array.data(), &data, byteSize); + break; + } + + // 4 bytes + case QMetaType::Float: { + T data = v.value<float>(); + memcpy(array.data(), &data, byteSize); + break; + } + case QMetaType::Int: { + T data = v.value<int>(); + memcpy(array.data(), &data, byteSize); + break; + + } + case QMetaType::UInt: { + qDebug() << "UINT"; + T data = v.value<uint>(); + memcpy(array.data(), &data, byteSize); + break; + } + + // 8 bytes + case QMetaType::Double: { + T data = v.value<double>(); + memcpy(array.data(), &data, byteSize); + break; + } + + // 2 floats + case QMetaType::QPointF: { + QPointF vv = v.value<QPointF>(); + T data = vv.x(); + memcpy(array.data(), &data, byteSize); + data = vv.y(); + memcpy(array.data() + byteSize, &data, byteSize); + break; + } + case QMetaType::QSizeF: { + QSizeF vv = v.value<QSizeF>(); + T data = vv.width(); + memcpy(array.data(), &data, byteSize); + data = vv.height(); + memcpy(array.data() + byteSize, &data, byteSize); + break; + } + + case QMetaType::QVector2D: { + QVector2D vv = v.value<QVector2D>(); + T data = vv.x(); + memcpy(array.data(), &data, byteSize); + data = vv.y(); + memcpy(array.data() + byteSize, &data, byteSize); + break; + } + + // 2 ints + case QMetaType::QPoint: { + QPointF vv = v.value<QPoint>(); + T data = vv.x(); + memcpy(array.data(), &data, byteSize); + data = vv.y(); + memcpy(array.data() + byteSize, &data, byteSize); + break; + } + + case QMetaType::QSize: { + QSize vv = v.value<QSize>(); + T data = vv.width(); + memcpy(array.data(), &data, byteSize); + data = vv.height(); + memcpy(array.data() + byteSize, &data, byteSize); + break; + } + + // 3 floats + case QMetaType::QVector3D: { + QVector3D vv = v.value<QVector3D>(); + T data = vv.x(); + memcpy(array.data(), &data, byteSize); + data = vv.y(); + memcpy(array.data() + byteSize, &data, byteSize); + data = vv.z(); + memcpy(array.data() + 2 * byteSize, &data, byteSize); + break; + } + + // 4 floats + case QMetaType::QVector4D: { + QVector4D vv = v.value<QVector4D>(); + T data = vv.x(); + memcpy(array.data(), &data, byteSize); + data = vv.y(); + memcpy(array.data() + byteSize, &data, byteSize); + data = vv.z(); + memcpy(array.data() + 2 * byteSize, &data, byteSize); + data = vv.w(); + memcpy(array.data() + 3 * byteSize, &data, byteSize); + break; + } + + case QMetaType::QQuaternion: { + + break; + } + + case QMetaType::QRectF: { + QRectF vv = v.value<QRectF>(); + T data = vv.x(); + memcpy(array.data(), &data, byteSize); + data = vv.y(); + memcpy(array.data() + byteSize, &data, byteSize); + data = vv.width(); + memcpy(array.data() + 2 * byteSize, &data, byteSize); + data = vv.height(); + memcpy(array.data() + 3 * byteSize, &data, byteSize); + break; + } + + case QMetaType::QColor: { + QColor vv = v.value<QColor>(); + T data = vv.redF(); + memcpy(array.data(), &data, byteSize); + data = vv.greenF(); + memcpy(array.data() + byteSize, &data, byteSize); + data = vv.blueF(); + memcpy(array.data() + 2 * byteSize, &data, byteSize); + data = vv.alphaF(); + memcpy(array.data() + 3 * byteSize, &data, byteSize); + break; + } + + // 4 ints + case QMetaType::QRect: { + QRectF vv = v.value<QRect>(); + T data = vv.x(); + memcpy(array.data(), &data, byteSize); + data = vv.y(); + memcpy(array.data() + byteSize, &data, byteSize); + data = vv.width(); + memcpy(array.data() + 2 * byteSize, &data, byteSize); + data = vv.height(); + memcpy(array.data() + 3 * byteSize, &data, byteSize); + break; + } + + // 16 floats + case QMetaType::QMatrix4x4: { + QMatrix4x4 mat = v.value<QMatrix4x4>(); + float *data = mat.data(); + for (int i = 0; i < 16; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + break; + } + + default: { + + float *data = nullptr; + if (v.userType() == QMatrix3x3Type) { + QMatrix3x3 mat = v.value<QMatrix3x3>(); + data = mat.data(); + for (int i = 0; i < 9; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else if (v.userType() == QMatrix2x2Type) { + QMatrix2x2 mat = v.value<QMatrix2x2>(); + data = mat.data(); + for (int i = 0; i < 4; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else if (v.userType() == QMatrix2x3Type) { + QMatrix2x3 mat = v.value<QMatrix2x3>(); + data = mat.data(); + for (int i = 0; i < 6; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else if (v.userType() == QMatrix3x2Type) { + QMatrix3x2 mat = v.value<QMatrix3x2>(); + data = mat.data(); + for (int i = 0; i < 6; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else if (v.userType() == QMatrix2x4Type) { + QMatrix2x4 mat = v.value<QMatrix2x4>(); + data = mat.data(); + for (int i = 0; i < 8; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else if (v.userType() == QMatrix4x2Type) { + QMatrix4x2 mat = v.value<QMatrix4x2>(); + data = mat.data(); + for (int i = 0; i < 8; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else if (v.userType() == QMatrix3x4Type) { + QMatrix3x4 mat = v.value<QMatrix3x4>(); + data = mat.data(); + for (int i = 0; i < 12; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else if (v.userType() == QMatrix4x3Type) { + QMatrix4x3 mat = v.value<QMatrix4x3>(); + data = mat.data(); + for (int i = 0; i < 12; i++) { + T d = data[i]; + memcpy(array.data() + i * byteSize, &d, byteSize); + } + } + else + qWarning() << Q_FUNC_INFO << "QVariant type conversion not handled for " << v.type(); + break; + } + + } + return array.constData(); + } + + + template<typename T> + static const T *valueArrayFromVariant(const QVariant &v, int count, int tupleSize) + { + uint byteSize = sizeof(T); + uint offset = byteSize * tupleSize; + static QVarLengthArray<char, 1024> uniformValuesArray(1024); + uniformValuesArray.resize(count * offset); + char *data = uniformValuesArray.data(); + memset(data, 0, uniformValuesArray.size()); + + QVariantList vList = v.toList(); + // Handles list of QVariant: usually arrays of float + if (!vList.isEmpty()) { + for (int i = 0; i < vList.length() && uint(i) * offset < uint(uniformValuesArray.size()); i++) { + const char *subBuffer = QGraphicsUtils::bytesFromVariant<T>(vList.at(i)); + memcpy(data + i * offset, subBuffer, offset); + } + } + else { + memcpy(data, QGraphicsUtils::bytesFromVariant<T>(v), offset); + } + return reinterpret_cast<const T *>(uniformValuesArray.constData()); + } + + template<typename T> + static void fillDataArray(void *buffer, const T *data, const ShaderUniform &description, int tupleSize) + { + uint offset = description.m_offset / sizeof(T); + uint stride = description.m_arrayStride / sizeof(T); + T *bufferData = (T*)buffer; + + for (int i = 0; i < description.m_size; ++i) { + for (int j = 0; j < tupleSize; j++) { + int idx = i * tupleSize + j; + bufferData[offset + j] = data[idx]; + } + offset += stride; + } + } + + template<typename T> + static void fillDataMatrixArray(void *buffer, const T *data, const ShaderUniform &description, int cols, int rows) + { + uint offset = description.m_offset / sizeof(T); + uint arrayStride = description.m_arrayStride / sizeof(T); + uint matrixStride = description.m_matrixStride / sizeof(T); + T *bufferData = (T*)buffer; + + for (int i = 0; i < description.m_size; ++i) { + for (int col = 0; col < cols; ++col) { + for (int row = 0; row < rows; ++row) { + int idx = i * cols * rows + rows * col + row; + bufferData[offset + row] = data[idx]; + } + offset += matrixStride; + } + offset += arrayStride; + } + } + +}; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_QGRAPHICSUTILS_P_H diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp new file mode 100644 index 000000000..9501b0b52 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp @@ -0,0 +1,1574 @@ +/**************************************************************************** +** +** 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/qcullface.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/material_p.h> +#include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/attribute_p.h> +#include <Qt3DRender/private/renderstates_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/rendertarget_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/qbuffer_p.h> +#include <Qt3DRender/private/stringtoint_p.h> +#include <gltexture_p.h> +#include <rendercommand_p.h> +#include <graphicshelperinterface_p.h> +#include <renderer_p.h> +#include <glresourcemanagers_p.h> +#include <renderbuffer_p.h> +#include <glshader_p.h> +#include <openglvertexarrayobject_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 <graphicshelpergl2_p.h> +#include <graphicshelpergl3_2_p.h> +#include <graphicshelpergl3_3_p.h> +#include <graphicshelpergl4_p.h> +#endif +#include <graphicshelperes2_p.h> +#include <graphicshelperes3_p.h> + +#include <private/qdebug_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; + +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; +} + +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; + } +} + +// Render States Helpers +template<typename GenericState> +void applyStateHelper(const GenericState *state, SubmissionContext *gc) +{ + Q_UNUSED(state); + Q_UNUSED(gc); +} + +template<> +void applyStateHelper<AlphaFunc>(const AlphaFunc *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->alphaTest(std::get<0>(values), std::get<1>(values)); +} + +template<> +void applyStateHelper<BlendEquationArguments>(const BlendEquationArguments *state, SubmissionContext *gc) +{ + const auto values = state->values(); + // Un-indexed BlendEquationArguments -> Use normal GL1.0 functions + if (std::get<5>(values) < 0) { + if (std::get<4>(values)) { + gc->openGLContext()->functions()->glEnable(GL_BLEND); + gc->openGLContext()->functions()->glBlendFuncSeparate(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); + } else { + gc->openGLContext()->functions()->glDisable(GL_BLEND); + } + } + // BlendEquationArguments for a particular Draw Buffer. Different behaviours for + // (1) 3.0-3.3: only enablei/disablei supported. + // (2) 4.0+: all operations supported. + // We just ignore blend func parameter for (1), so no warnings get + // printed. + else { + if (std::get<4>(values)) { + gc->enablei(GL_BLEND, std::get<5>(values)); + if (gc->supportsDrawBuffersBlend()) { + gc->blendFuncSeparatei(std::get<5>(values), std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); + } + } else { + gc->disablei(GL_BLEND, std::get<5>(values)); + } + } +} + +template<> +void applyStateHelper<BlendEquation>(const BlendEquation *state, SubmissionContext *gc) +{ + gc->blendEquation(std::get<0>(state->values())); +} + +template<> +void applyStateHelper<MSAAEnabled>(const MSAAEnabled *state, SubmissionContext *gc) +{ + gc->setMSAAEnabled(std::get<0>(state->values())); +} + +template<> +void applyStateHelper<DepthRange>(const DepthRange *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->depthRange(std::get<0>(values), std::get<1>(values)); +} + +template<> +void applyStateHelper<DepthTest>(const DepthTest *state, SubmissionContext *gc) +{ + gc->depthTest(std::get<0>(state->values())); +} + +template<> +void applyStateHelper<RasterMode>(const RasterMode *state, SubmissionContext *gc) +{ + gc->rasterMode(std::get<0>(state->values()), std::get<1>(state->values())); +} + +template<> +void applyStateHelper<NoDepthMask>(const NoDepthMask *state, SubmissionContext *gc) +{ + gc->depthMask(std::get<0>(state->values())); +} + +template<> +void applyStateHelper<CullFace>(const CullFace *state, SubmissionContext *gc) +{ + const auto values = state->values(); + if (std::get<0>(values) == QCullFace::NoCulling) { + gc->openGLContext()->functions()->glDisable(GL_CULL_FACE); + } else { + gc->openGLContext()->functions()->glEnable(GL_CULL_FACE); + gc->openGLContext()->functions()->glCullFace(std::get<0>(values)); + } +} + +template<> +void applyStateHelper<FrontFace>(const FrontFace *state, SubmissionContext *gc) +{ + gc->frontFace(std::get<0>(state->values())); +} + +template<> +void applyStateHelper<ScissorTest>(const ScissorTest *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->openGLContext()->functions()->glEnable(GL_SCISSOR_TEST); + gc->openGLContext()->functions()->glScissor(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); +} + +template<> +void applyStateHelper<StencilTest>(const StencilTest *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->openGLContext()->functions()->glEnable(GL_STENCIL_TEST); + gc->openGLContext()->functions()->glStencilFuncSeparate(GL_FRONT, std::get<0>(values), std::get<1>(values), std::get<2>(values)); + gc->openGLContext()->functions()->glStencilFuncSeparate(GL_BACK, std::get<3>(values), std::get<4>(values), std::get<5>(values)); +} + +template<> +void applyStateHelper<AlphaCoverage>(const AlphaCoverage *, SubmissionContext *gc) +{ + gc->setAlphaCoverageEnabled(true); +} + +template<> +void applyStateHelper<PointSize>(const PointSize *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->pointSize(std::get<0>(values), std::get<1>(values)); +} + + +template<> +void applyStateHelper<PolygonOffset>(const PolygonOffset *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->openGLContext()->functions()->glEnable(GL_POLYGON_OFFSET_FILL); + gc->openGLContext()->functions()->glPolygonOffset(std::get<0>(values), std::get<1>(values)); +} + +template<> +void applyStateHelper<ColorMask>(const ColorMask *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->openGLContext()->functions()->glColorMask(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); +} + +template<> +void applyStateHelper<ClipPlane>(const ClipPlane *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->enableClipPlane(std::get<0>(values)); + gc->setClipPlane(std::get<0>(values), std::get<1>(values), std::get<2>(values)); +} + +template<> +void applyStateHelper<SeamlessCubemap>(const SeamlessCubemap *, SubmissionContext *gc) +{ + gc->setSeamlessCubemap(true); +} + +template<> +void applyStateHelper<StencilOp>(const StencilOp *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->openGLContext()->functions()->glStencilOpSeparate(GL_FRONT, std::get<0>(values), std::get<1>(values), std::get<2>(values)); + gc->openGLContext()->functions()->glStencilOpSeparate(GL_BACK, std::get<3>(values), std::get<4>(values), std::get<5>(values)); +} + +template<> +void applyStateHelper<StencilMask>(const StencilMask *state, SubmissionContext *gc) +{ + const auto values = state->values(); + gc->openGLContext()->functions()->glStencilMaskSeparate(GL_FRONT, std::get<0>(values)); + gc->openGLContext()->functions()->glStencilMaskSeparate(GL_BACK, std::get<1>(values)); +} + +template<> +void applyStateHelper<Dithering>(const Dithering *, SubmissionContext *gc) +{ + gc->openGLContext()->functions()->glEnable(GL_DITHER); +} + +#ifndef GL_LINE_SMOOTH +#define GL_LINE_SMOOTH 0x0B20 +#endif + +template<> +void applyStateHelper<LineWidth>(const LineWidth *state, SubmissionContext *gc) +{ + const auto values = state->values(); + if (std::get<1>(values)) + gc->openGLContext()->functions()->glEnable(GL_LINE_SMOOTH); + else + gc->openGLContext()->functions()->glDisable(GL_LINE_SMOOTH); + + gc->openGLContext()->functions()->glLineWidth(std::get<0>(values)); +} + +} // anonymous + + +SubmissionContext::SubmissionContext() + : GraphicsContext() + , m_ownCurrent(true) + , m_id(nextFreeContextId()) + , m_surface(nullptr) + , m_activeShader(nullptr) + , 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)) +{ + 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_textureContext.initialize(this); + m_imageContext.initialize(this); +} + +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(); + +#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 (!isInitialized()) + initialize(); + initializeHelpers(m_surface); + + // 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_boundArrayBuffer = nullptr; + return true; +} + +void SubmissionContext::endDrawing(bool swapBuffers) +{ + if (swapBuffers) + m_gl->swapBuffers(m_surface); + if (m_ownCurrent) + m_gl->doneCurrent(); + m_textureContext.endDrawing(); + m_imageContext.endDrawing(); +} + +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->glResourceManagers()->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. + if (rTex) { + needsResize |= rTex->size() != s; + if (isActiveRenderTarget && attachment.m_point == QRenderTargetOutput::Color0) + m_renderTargetFormat = rTex->properties().format; + } + } + } + + if (needsResize) { + m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); + bindFrameBufferAttachmentHelper(fboId, attachments); + } + + return fboId; +} + +QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const +{ + QSize renderTargetSize; + if (m_activeFBO != m_defaultFBO) { + // For external FBOs we may not have a m_renderTargets entry. + if (m_renderTargetsSize.contains(m_activeFBO)) { + renderTargetSize = m_renderTargetsSize[m_activeFBO]; + } else if (surfaceSize.isValid()) { + renderTargetSize = surfaceSize; + } else { + // External FBO (when used with QtQuick2 Scene3D) + + // Query FBO color attachment 0 size + GLint attachmentObjectType = GL_NONE; + GLint attachment0Name = 0; + m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + &attachmentObjectType); + m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &attachment0Name); + + if (attachmentObjectType == GL_RENDERBUFFER && m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval)) + renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name); + else if (attachmentObjectType == GL_TEXTURE && m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval)) + // Assumes texture level 0 and GL_TEXTURE_2D target + renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name, GL_TEXTURE_2D); + else + return renderTargetSize; + } + } else { + renderTargetSize = m_surface->size(); + if (m_surface->surfaceClass() == QSurface::Window) { + const float dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio(); + renderTargetSize *= dpr; + } + } + return renderTargetSize; +} + +QImage SubmissionContext::readFramebuffer(const QRect &rect) +{ + QImage img; + const unsigned int area = rect.width() * rect.height(); + unsigned int bytes; + GLenum format, type; + QImage::Format imageFormat; + uint stride; + + /* format value should match GL internalFormat */ + GLenum internalFormat = m_renderTargetFormat; + + switch (m_renderTargetFormat) { + case QAbstractTexture::RGBAFormat: + case QAbstractTexture::RGBA8_SNorm: + case QAbstractTexture::RGBA8_UNorm: + case QAbstractTexture::RGBA8U: + case QAbstractTexture::SRGB8_Alpha8: +#ifdef QT_OPENGL_ES_2 + format = GL_RGBA; + imageFormat = QImage::Format_RGBA8888_Premultiplied; +#else + format = GL_BGRA; + imageFormat = QImage::Format_ARGB32_Premultiplied; + internalFormat = GL_RGBA8; +#endif + type = GL_UNSIGNED_BYTE; + bytes = area * 4; + stride = rect.width() * 4; + break; + case QAbstractTexture::SRGB8: + case QAbstractTexture::RGBFormat: + case QAbstractTexture::RGB8U: + case QAbstractTexture::RGB8_UNorm: +#ifdef QT_OPENGL_ES_2 + format = GL_RGBA; + imageFormat = QImage::Format_RGBX8888; +#else + format = GL_BGRA; + imageFormat = QImage::Format_RGB32; + internalFormat = GL_RGB8; +#endif + type = GL_UNSIGNED_BYTE; + bytes = area * 4; + stride = rect.width() * 4; + break; +#ifndef QT_OPENGL_ES_2 + case QAbstractTexture::RG11B10F: + bytes = area * 4; + format = GL_RGB; + type = GL_UNSIGNED_INT_10F_11F_11F_REV; + imageFormat = QImage::Format_RGB30; + stride = rect.width() * 4; + break; + case QAbstractTexture::RGB10A2: + bytes = area * 4; + format = GL_RGBA; + type = GL_UNSIGNED_INT_2_10_10_10_REV; + imageFormat = QImage::Format_A2BGR30_Premultiplied; + stride = rect.width() * 4; + break; + case QAbstractTexture::R5G6B5: + bytes = area * 2; + format = GL_RGB; + type = GL_UNSIGNED_SHORT; + internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV; + imageFormat = QImage::Format_RGB16; + stride = rect.width() * 2; + break; + case QAbstractTexture::RGBA16F: + case QAbstractTexture::RGBA16U: + case QAbstractTexture::RGBA32F: + case QAbstractTexture::RGBA32U: + bytes = area * 16; + format = GL_RGBA; + type = GL_FLOAT; + imageFormat = QImage::Format_ARGB32_Premultiplied; + stride = rect.width() * 16; + break; +#endif + default: + auto warning = qWarning(); + warning << "Unable to convert"; + QtDebugUtils::formatQEnum(warning, m_renderTargetFormat); + warning << "render target texture format to QImage."; + return img; + } + + GLint samples = 0; + m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples); + if (samples > 0 && !m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) { + qCWarning(Backend) << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; " + "Required feature BlitFramebuffer is missing."; + return img; + } + + img = QImage(rect.width(), rect.height(), imageFormat); + + QScopedArrayPointer<uchar> data(new uchar [bytes]); + + if (samples > 0) { + // resolve multisample-framebuffer to renderbuffer and read pixels from it + GLuint fbo, rb; + QOpenGLFunctions *gl = m_gl->functions(); + gl->glGenFramebuffers(1, &fbo); + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + gl->glGenRenderbuffers(1, &rb); + gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); + gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(), rect.height()); + gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); + + const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + gl->glDeleteRenderbuffers(1, &rb); + gl->glDeleteFramebuffers(1, &fbo); + qCWarning(Backend) << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status; + return img; + } + + m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(), + 0, 0, rect.width(), rect.height(), + GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data()); + + copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); + + gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); + gl->glDeleteRenderbuffers(1, &rb); + gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO); + gl->glDeleteFramebuffers(1, &fbo); + } else { + // read pixels directly from framebuffer + m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), format, type, data.data()); + copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); + } + + return img; +} + +void SubmissionContext::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_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); + + releaseOpenGL(); + m_gl = ctx; +} + +// Called only from RenderThread +bool SubmissionContext::activateShader(GLShader *shader) +{ + if (shader->shaderProgram() != m_activeShader) { + // Ensure material uniforms are re-applied + m_material = nullptr; + + m_activeShader = shader->shaderProgram(); + if (Q_LIKELY(m_activeShader != nullptr)) { + m_activeShader->bind(); + } else { + m_glHelper->useProgram(0); + qWarning() << "No shader program found"; + return false; + } + } + return true; +} + +void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments) +{ + // 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->glResourceManagers()->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->getGLTexture() : nullptr; + if (glTex != nullptr) { + // The texture can not be rendered simultaniously by another renderer + Q_ASSERT(!rTex->isExternalRenderingEnabled()); + if (fboSize.isEmpty()) + fboSize = QSize(glTex->width(), glTex->height()); + else + fboSize = QSize(qMin(fboSize.width(), glTex->width()), qMin(fboSize.height(), glTex->height())); + m_glHelper->bindFrameBufferAttachment(glTex, attachment); + } + } else { + RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr; + if (renderBuffer) { + if (fboSize.isEmpty()) + fboSize = QSize(renderBuffer->width(), renderBuffer->height()); + else + fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()), qMin(fboSize.height(), renderBuffer->height())); + m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment); + } + } + } + m_renderTargetsSize.insert(fboId, fboSize); +} + +void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments) +{ + const QVector<int> activeDrawBuffers = attachments.getGlDrawBuffers(); + + if (m_glHelper->checkFrameBufferComplete()) { + if (activeDrawBuffers.size() > 1) {// We need MRT + if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) { + // Set up MRT, glDrawBuffers... + m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data()); + } + } + } else { + qCWarning(Backend) << "FBO incomplete"; + } +} + + +void SubmissionContext::setActiveMaterial(Material *rmat) +{ + if (m_material == rmat) + return; + + m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial); + m_imageContext.deactivateImages(); + m_material = rmat; +} + +void SubmissionContext::setCurrentStateSet(RenderStateSet *ss) +{ + if (ss == m_stateSet) + return; + if (ss) + applyStateSet(ss); + m_stateSet = ss; +} + +RenderStateSet *SubmissionContext::currentStateSet() const +{ + return m_stateSet; +} + +void SubmissionContext::applyState(const StateVariant &stateVariant) +{ + switch (stateVariant.type) { + + case AlphaCoverageStateMask: { + applyStateHelper<AlphaCoverage>(static_cast<const AlphaCoverage *>(stateVariant.constState()), this); + break; + } + case AlphaTestMask: { + applyStateHelper<AlphaFunc>(static_cast<const AlphaFunc *>(stateVariant.constState()), this); + break; + } + case BlendStateMask: { + applyStateHelper<BlendEquation>(static_cast<const BlendEquation *>(stateVariant.constState()), this); + break; + } + case BlendEquationArgumentsMask: { + applyStateHelper<BlendEquationArguments>(static_cast<const BlendEquationArguments *>(stateVariant.constState()), this); + break; + } + case MSAAEnabledStateMask: { + applyStateHelper<MSAAEnabled>(static_cast<const MSAAEnabled *>(stateVariant.constState()), this); + break; + } + + case CullFaceStateMask: { + applyStateHelper<CullFace>(static_cast<const CullFace *>(stateVariant.constState()), this); + break; + } + + case DepthWriteStateMask: { + applyStateHelper<NoDepthMask>(static_cast<const NoDepthMask *>(stateVariant.constState()), this); + break; + } + + case DepthTestStateMask: { + applyStateHelper<DepthTest>(static_cast<const DepthTest *>(stateVariant.constState()), this); + break; + } + + case DepthRangeMask: { + applyStateHelper<DepthRange>(static_cast<const DepthRange *>(stateVariant.constState()), this); + break; + } + + case RasterModeMask: { + applyStateHelper<RasterMode>(static_cast<const RasterMode *>(stateVariant.constState()), this); + break; + } + + case FrontFaceStateMask: { + applyStateHelper<FrontFace>(static_cast<const FrontFace *>(stateVariant.constState()), this); + break; + } + + case ScissorStateMask: { + applyStateHelper<ScissorTest>(static_cast<const ScissorTest *>(stateVariant.constState()), this); + break; + } + + case StencilTestStateMask: { + applyStateHelper<StencilTest>(static_cast<const StencilTest *>(stateVariant.constState()), this); + break; + } + + case PointSizeMask: { + applyStateHelper<PointSize>(static_cast<const PointSize *>(stateVariant.constState()), this); + break; + } + + case PolygonOffsetStateMask: { + applyStateHelper<PolygonOffset>(static_cast<const PolygonOffset *>(stateVariant.constState()), this); + break; + } + + case ColorStateMask: { + applyStateHelper<ColorMask>(static_cast<const ColorMask *>(stateVariant.constState()), this); + break; + } + + case ClipPlaneMask: { + applyStateHelper<ClipPlane>(static_cast<const ClipPlane *>(stateVariant.constState()), this); + break; + } + + case SeamlessCubemapMask: { + applyStateHelper<SeamlessCubemap>(static_cast<const SeamlessCubemap *>(stateVariant.constState()), this); + break; + } + + case StencilOpMask: { + applyStateHelper<StencilOp>(static_cast<const StencilOp *>(stateVariant.constState()), this); + break; + } + + case StencilWriteStateMask: { + applyStateHelper<StencilMask>(static_cast<const StencilMask *>(stateVariant.constState()), this); + break; + } + + case DitheringStateMask: { + applyStateHelper<Dithering>(static_cast<const Dithering *>(stateVariant.constState()), this); + break; + } + + case LineWidthMask: { + applyStateHelper<LineWidth>(static_cast<const LineWidth *>(stateVariant.constState()), this); + break; + } + default: + Q_UNREACHABLE(); + } +} + +void SubmissionContext::resetMasked(qint64 maskOfStatesToReset) +{ + // TO DO -> Call gcHelper methods instead of raw GL + // QOpenGLFunctions shouldn't be used here directly + QOpenGLFunctions *funcs = m_gl->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 & DepthRangeMask) + depthRange(0.0f, 1.0f); + + 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) + setAlphaCoverageEnabled(false); + + if (maskOfStatesToReset & PointSizeMask) + 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 = maxClipPlaneCount(); + for (GLint i = 0; i < max; ++i) + disableClipPlane(i); + } + + if (maskOfStatesToReset & SeamlessCubemapMask) + setSeamlessCubemap(false); + + if (maskOfStatesToReset & StencilOpMask) + funcs->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + if (maskOfStatesToReset & LineWidthMask) + funcs->glLineWidth(1.0f); + +#ifndef QT_OPENGL_ES_2 + if (maskOfStatesToReset & RasterModeMask) + m_glHelper->rasterMode(GL_FRONT_AND_BACK, GL_FILL); +#endif +} + +void SubmissionContext::applyStateSet(RenderStateSet *ss) +{ + RenderStateSet* previousStates = currentStateSet(); + + const StateMaskSet invOurState = ~ss->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(ss->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); + + // Apply states that weren't in the previous state or that have + // different values + const QVector<StateVariant> statesToSet = ss->states(); + for (const StateVariant &ds : statesToSet) { + if (previousStates && previousStates->contains(ds)) + continue; + applyState(ds); + } +} + +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); + } +} + +GLFence SubmissionContext::fenceSync() +{ + return m_glHelper->fenceSync(); +} + +void SubmissionContext::clientWaitSync(GLFence sync, GLuint64 nanoSecTimeout) +{ + qDebug() << Q_FUNC_INFO << sync; + m_glHelper->clientWaitSync(sync, nanoSecTimeout); +} + +void SubmissionContext::waitSync(GLFence sync) +{ + qDebug() << Q_FUNC_INFO << sync; + m_glHelper->waitSync(sync); +} + +bool SubmissionContext::wasSyncSignaled(GLFence sync) +{ + return m_glHelper->wasSyncSignaled(sync); +} + +void SubmissionContext::deleteSync(GLFence sync) +{ + m_glHelper->deleteSync(sync); +} + +// It will be easier if the QGraphicContext applies the QUniformPack +// than the other way around +bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) +{ + static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); + static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); + // Activate textures and update TextureUniform in the pack + // with the correct textureUnit + + // Set the pinned texture of the previous material texture + // to pinable so that we should easily find an available texture unit + m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial); + // Update the uniforms with the correct texture unit id's + PackUniformHash &uniformValues = parameterPack.uniforms(); + + // Fill Texture Uniform Value with proper texture units + // so that they can be applied as regular uniforms in a second step + for (int i = 0; i < parameterPack.textures().size(); ++i) { + const ShaderParameterPack::NamedResource &namedTex = parameterPack.textures().at(i); + // Given a Texture QNodeId, we retrieve the associated shared GLTexture + if (uniformValues.contains(namedTex.glslNameId)) { + GLTexture *t = m_renderer->glResourceManagers()->glTextureManager()->lookupResource(namedTex.nodeId); + if (t != nullptr) { + UniformValue &texUniform = uniformValues.value(namedTex.glslNameId); + if (texUniform.valueType() == UniformValue::TextureValue) { + const int texUnit = m_textureContext.activateTexture(TextureSubmissionContext::TextureScopeMaterial, m_gl, t); + texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit; + if (texUnit == -1) { + if (namedTex.glslNameId != irradianceId && + namedTex.glslNameId != specularId) { + // Only return false if we are not dealing with env light textures + qCWarning(Backend) << "Unable to find suitable Texture Unit"; + return false; + } + } + } + } + } + } + + // Fill Image Uniform Value with proper image units + // so that they can be applied as regular uniforms in a second step + for (int i = 0; i < parameterPack.images().size(); ++i) { + const ShaderParameterPack::NamedResource &namedTex = parameterPack.images().at(i); + // Given a Texture QNodeId, we retrieve the associated shared GLTexture + if (uniformValues.contains(namedTex.glslNameId)) { + ShaderImage *img = m_renderer->nodeManagers()->shaderImageManager()->lookupResource(namedTex.nodeId); + if (img != nullptr) { + GLTexture *t = m_renderer->glResourceManagers()->glTextureManager()->lookupResource(img->textureId()); + if (t == nullptr) { + qCWarning(Backend) << "Shader Image referencing invalid texture"; + continue; + } else { + UniformValue &imgUniform = uniformValues.value(namedTex.glslNameId); + if (imgUniform.valueType() == UniformValue::ShaderImageValue) { + const int imgUnit = m_imageContext.activateImage(img, t); + imgUniform.data<int>()[namedTex.uniformArrayIndex] = imgUnit; + if (imgUnit == -1) { + qCWarning(Backend) << "Unable to bind Image to Texture"; + 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(); + for (const BlockToSSBO b : blockToSSBOs) { + Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); + GLBuffer *ssbo = glBufferForRenderBuffer(cpuBuffer); + // bindShaderStorageBlock + // This is currently not required as we are introspecting the bindingIndex + // value from the shaders and not replacing them, making such a call useless + // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex); + bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex); + // Needed to avoid conflict where the buffer would already + // be bound as a VertexArray + bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer); + ssbo->bindBufferBase(this, b.m_bindingIndex, 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); + 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.value(uniform.m_nameId); + + // skip invalid textures/images + if ((v.valueType() == UniformValue::TextureValue || + v.valueType() == UniformValue::ShaderImageValue) && + *v.constData<int>() == -1) + continue; + + applyUniform(uniform, v); + } + // if not all data is valid, the next frame will be rendered immediately + return true; +} + +void SubmissionContext::enableAttribute(const VAOVertexAttribute &attr) +{ + // Bind buffer within the current VAO + GLBuffer *buf = m_renderer->glResourceManagers()->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->glResourceManagers()->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(); + } + + Q_ASSERT(!glBufferHandle.isNull()); + VAOVertexAttribute attr; + attr.bufferHandle = glBufferHandle; + attr.attributeType = attributeType; + attr.dataType = attributeDataType; + attr.divisor = attribute->divisor(); + attr.vertexSize = attribute->vertexSize() / attrCount; + attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize); + attr.shaderDataType = attributeDescription->m_type; + + for (int i = 0; i < attrCount; i++) { + attr.location = location + i; + attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize); + + 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); + 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->glResourceManagers()->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->glResourceManagers()->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->glResourceManagers()->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->glResourceManagers()->glBufferManager()->data(glBuffHandle); + + Q_ASSERT(glBuff); + // Destroy the GPU resource + glBuff->destroy(this); + // Destroy the GLBuffer instance + m_renderer->glResourceManagers()->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) +{ + if (!m_renderBufferHash.contains(buf->peerId())) + m_renderBufferHash.insert(buf->peerId(), createGLBufferFor(buf)); + return m_renderer->glResourceManagers()->glBufferManager()->data(m_renderBufferHash.value(buf->peerId())); +} + +HGLBuffer SubmissionContext::createGLBufferFor(Buffer *buffer) +{ + GLBuffer *b = m_renderer->glResourceManagers()->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"; + + return m_renderer->glResourceManagers()->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) { + // Note that we use glDrawBuffers, not glDrawBuffer. The + // latter is not available with GLES. + const int buf = outputAttachmentPoint; + drawBuffers(1, &buf); + } + + // Blit framebuffer + const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR; + m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, + mode); + + // Reset draw buffer + bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw); + if (outputAttachmentPoint != QRenderTargetOutput::Color0) { + const int buf = QRenderTargetOutput::Color0; + drawBuffers(1, &buf); + } +} + +} // namespace Render +} // namespace Qt3DRender of namespace + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h new file mode 100644 index 000000000..16eca07ea --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** 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 <glbuffer_p.h> +#include <glfence_p.h> +#include <graphicscontext_p.h> +#include <texturesubmissioncontext_p.h> +#include <imagesubmissioncontext_p.h> +#include <Qt3DRender/qclearbuffers.h> +#include <Qt3DRender/qattribute.h> +#include <Qt3DRender/private/handle_types_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; +struct StateVariant; + +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 releaseOpenGL(); + void setOpenGLContext(QOpenGLContext* ctx); + + // Viewport + void setViewport(const QRectF &viewport, const QSize &surfaceSize); + QRectF viewport() const { return m_viewport; } + + // Shaders + bool activateShader(GLShader *shader); + 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); + + // 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); + + // Parameters + bool setParameters(ShaderParameterPack ¶meterPack); + + // RenderState + void setCurrentStateSet(RenderStateSet* ss); + RenderStateSet *currentStateSet() const; + void applyState(const StateVariant &state); + + void resetMasked(qint64 maskOfStatesToReset); + void applyStateSet(RenderStateSet *ss); + + // Wrappers + void clearColor(const QColor &color); + void clearDepthValue(float depth); + void clearStencilValue(int stencil); + + + // Fences + GLFence fenceSync(); + void clientWaitSync(GLFence sync, GLuint64 nanoSecTimeout); + void waitSync(GLFence sync); + bool wasSyncSignaled(GLFence sync); + void deleteSync(GLFence sync); + +private: + void initialize(); + + // Material + Material* activeMaterial() const { return m_material; } + void setActiveMaterial(Material* rmat); + + // FBO + void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments); + void activateDrawBuffers(const AttachmentPack &attachments); + void resolveRenderTargetFormat(); + GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments); + GLuint updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget); + + // Buffers + HGLBuffer createGLBufferFor(Buffer *buffer); + void uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer = false); + QByteArray downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b); + bool bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type); + + bool m_ownCurrent; + const unsigned int m_id; + QSurface *m_surface; + QSize m_surfaceSize; + + QOpenGLShaderProgram *m_activeShader; + + QHash<Qt3DCore::QNodeId, HGLBuffer> m_renderBufferHash; + QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets; + QHash<GLuint, QSize> m_renderTargetsSize; + QAbstractTexture::TextureFormat m_renderTargetFormat; + + // 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; + + TextureSubmissionContext m_textureContext; + ImageSubmissionContext m_imageContext; + + // Attributes + friend class OpenGLVertexArrayObject; + + 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/plugins/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp new file mode 100644 index 000000000..4f2ff80f5 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "texturesubmissioncontext_p.h" + +#include <graphicscontext_p.h> +#include <gltexture_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class TextureExtRendererLocker +{ +public: + static void lock(GLTexture *tex) + { + if (!tex->isExternalRenderingEnabled()) + return; + if (s_lockHash.keys().contains(tex)) { + ++s_lockHash[tex]; + } else { + tex->externalRenderingLock()->lock(); + s_lockHash[tex] = 1; + } + } + static void unlock(GLTexture *tex) + { + if (!tex->isExternalRenderingEnabled()) + return; + if (!s_lockHash.keys().contains(tex)) + return; + + --s_lockHash[tex]; + if (s_lockHash[tex] == 0) { + s_lockHash.remove(tex); + tex->externalRenderingLock()->unlock(); + } + } +private: + static QHash<GLTexture*, int> s_lockHash; +}; + +QHash<GLTexture*, int> TextureExtRendererLocker::s_lockHash = QHash<GLTexture*, int>(); + + +TextureSubmissionContext::TextureSubmissionContext() +{ + +} + +TextureSubmissionContext::~TextureSubmissionContext() +{ + +} + +void TextureSubmissionContext::initialize(GraphicsContext *context) +{ + m_activeTextures.resize(context->maxTextureUnitsCount()); +} + +void TextureSubmissionContext::endDrawing() +{ + decayTextureScores(); + for (int i = 0; i < m_activeTextures.size(); ++i) + if (m_activeTextures[i].texture) + TextureExtRendererLocker::unlock(m_activeTextures[i].texture); +} + +int TextureSubmissionContext::activateTexture(TextureSubmissionContext::TextureScope scope, + QOpenGLContext *m_gl, + GLTexture *tex) +{ + // 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 + const int onUnit = assignUnitForTexture(tex); + + // check we didn't overflow the available units + if (onUnit == -1) + return -1; + + const int sharedTextureId = tex->sharedTextureId(); + // We have a valid texture id provided by a shared context + if (sharedTextureId > 0) { + m_gl->functions()->glActiveTexture(GL_TEXTURE0 + onUnit); + const QAbstractTexture::Target target = tex->properties().target; + // For now we know that target values correspond to the GL values + m_gl->functions()->glBindTexture(target, tex->sharedTextureId()); + } else { + // Texture must have been created and updated at this point + QOpenGLTexture *glTex = tex->getGLTexture(); + if (glTex == nullptr) + return -1; + glTex->bind(uint(onUnit)); + } + if (m_activeTextures[onUnit].texture != tex) { + if (m_activeTextures[onUnit].texture) + TextureExtRendererLocker::unlock(m_activeTextures[onUnit].texture); + m_activeTextures[onUnit].texture = tex; + TextureExtRendererLocker::lock(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->getGLTexture()->textureId() << "on unit" << onUnit; +#endif + + m_activeTextures[onUnit].score = 200; + m_activeTextures[onUnit].pinned = true; + m_activeTextures[onUnit].scope = scope; + + return onUnit; +} + +void TextureSubmissionContext::deactivateTexturesWithScope(TextureSubmissionContext::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 TextureSubmissionContext::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. + */ +int TextureSubmissionContext::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; + } + + for (int u=0; u<m_activeTextures.size(); ++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 TextureSubmissionContext::decayTextureScores() +{ + for (int u = 0; u < m_activeTextures.size(); u++) + m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0); +} + +} // namespace Render +} // namespace Qt3DRender of namespace + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/graphicshelpers/texturesubmissioncontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/texturesubmissioncontext_p.h new file mode 100644 index 000000000..3c84fe558 --- /dev/null +++ b/src/plugins/renderers/opengl/graphicshelpers/texturesubmissioncontext_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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_TEXTURESUBMISSIONCONTEXT_H +#define QT3DRENDER_RENDER_TEXTURESUBMISSIONCONTEXT_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 <QVector> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; + +namespace Qt3DRender { +namespace Render { + +class GraphicsContext; +class GLTexture; + +class Q_AUTOTEST_EXPORT TextureSubmissionContext +{ +public: + enum TextureScope + { + TextureScopeMaterial = 0, + TextureScopeTechnique + // per-pass for deferred rendering? + }; + + TextureSubmissionContext(); + ~TextureSubmissionContext(); + + void initialize(GraphicsContext *context); + void endDrawing(); + int activateTexture(TextureScope scope, QOpenGLContext *gl, GLTexture* tex); + void deactivateTexture(GLTexture *tex); + void deactivateTexturesWithScope(TextureScope ts); + +private: + void decayTextureScores(); + int assignUnitForTexture(GLTexture* tex); + + // active textures, indexed by texture unit + struct ActiveTexture { + GLTexture *texture = nullptr; + int score = 0; + TextureScope scope = TextureScopeMaterial; + bool pinned = false; + }; + QVector<ActiveTexture> m_activeTextures; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_TEXTURESUBMISSIONCONTEXT_H diff --git a/src/plugins/renderers/opengl/io/glbuffer.cpp b/src/plugins/renderers/opengl/io/glbuffer.cpp new file mode 100644 index 000000000..53fc50cb9 --- /dev/null +++ b/src/plugins/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 <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/plugins/renderers/opengl/io/glbuffer_p.h b/src/plugins/renderers/opengl/io/glbuffer_p.h new file mode 100644 index 000000000..731634b6b --- /dev/null +++ b/src/plugins/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/plugins/renderers/opengl/io/io.pri b/src/plugins/renderers/opengl/io/io.pri new file mode 100644 index 000000000..462978c4d --- /dev/null +++ b/src/plugins/renderers/opengl/io/io.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/glbuffer.cpp + +HEADERS += \ + $$PWD/glbuffer_p.h + diff --git a/src/plugins/renderers/opengl/jobs/filtercompatibletechniquejob.cpp b/src/plugins/renderers/opengl/jobs/filtercompatibletechniquejob.cpp new file mode 100644 index 000000000..95e8862ef --- /dev/null +++ b/src/plugins/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/job_common_p.h> +#include <renderer_p.h> +#include <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/plugins/renderers/opengl/jobs/filtercompatibletechniquejob_p.h b/src/plugins/renderers/opengl/jobs/filtercompatibletechniquejob_p.h new file mode 100644 index 000000000..27b3d4ed2 --- /dev/null +++ b/src/plugins/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 Q_3DRENDERSHARED_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/plugins/renderers/opengl/jobs/jobs.pri b/src/plugins/renderers/opengl/jobs/jobs.pri new file mode 100644 index 000000000..d80b8bfd9 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/jobs.pri @@ -0,0 +1,17 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/filtercompatibletechniquejob.cpp \ + $$PWD/materialparametergathererjob.cpp \ + $$PWD/renderviewcommandbuilderjob.cpp \ + $$PWD/renderviewcommandupdaterjob.cpp \ + $$PWD/renderviewinitializerjob.cpp \ + $$PWD/renderviewjobutils.cpp + +HEADERS += \ + $$PWD/filtercompatibletechniquejob_p.h \ + $$PWD/materialparametergathererjob_p.h \ + $$PWD/renderviewcommandbuilderjob_p.h \ + $$PWD/renderviewcommandupdaterjob_p.h \ + $$PWD/renderviewinitializerjob_p.h \ + $$PWD/renderviewjobutils_p.h diff --git a/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp b/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp new file mode 100644 index 000000000..e1f8aa403 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::MaterialParameterGathering, materialParameterGathererCounter++) +} + +// TechniqueFilter / RenderPassFilter + +// Parameters from Material/Effect/Technique + +// Note: we could maybe improve that by having a smart update when we detect +// that a parameter value has changed. That might require way more book keeping +// which might make this solution a bit too complex + +// The fact that this can now be performed in parallel should already provide a big +// improvement +void MaterialParameterGathererJob::run() +{ + for (const HMaterial &materialHandle : qAsConst(m_handles)) { + Material *material = m_manager->materialManager()->data(materialHandle); + + if (Q_UNLIKELY(!material->isEnabled())) + continue; + + Effect *effect = m_manager->effectManager()->lookupResource(material->effect()); + Technique *technique = findTechniqueForEffect(m_manager, m_techniqueFilter, effect); + + if (Q_LIKELY(technique != nullptr)) { + RenderPassList passes = findRenderPassesForTechnique(m_manager, m_renderPassFilter, technique); + if (Q_LIKELY(passes.size() > 0)) { + // Order set: + // 1 Pass Filter + // 2 Technique Filter + // 3 Material + // 4 Effect + // 5 Technique + // 6 RenderPass + + // Add Parameters define in techniqueFilter and passFilter + // passFilter have priority over techniqueFilter + + ParameterInfoList parameters; + // Doing the reserve allows a gain of 0.5ms on some of the demo examples + parameters.reserve(likelyNumberOfParameters); + + if (m_renderPassFilter) + parametersFromParametersProvider(¶meters, m_manager->parameterManager(), + m_renderPassFilter); + if (m_techniqueFilter) + parametersFromParametersProvider(¶meters, m_manager->parameterManager(), + m_techniqueFilter); + // Get the parameters for our selected rendering setup (override what was defined in the technique/pass filter) + parametersFromMaterialEffectTechnique(¶meters, 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/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h b/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h new file mode 100644 index 000000000..9fe0cdfee --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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/qt3drender_global_p.h> +#include <renderviewjobutils_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 Q_3DRENDERSHARED_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 const QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> &materialToPassAndParameter() Q_DECL_NOTHROW { return m_parameters; } + inline void setHandles(const QVector<HMaterial> &handles) Q_DECL_NOTHROW { m_handles = handles; } + + inline TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_techniqueFilter; } + inline RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_renderPassFilter; } + + void run() final; + +private: + NodeManagers *m_manager; + TechniqueFilter *m_techniqueFilter; + RenderPassFilter *m_renderPassFilter; + + // Material id to array of RenderPasse with parameters + MaterialParameterGathererData m_parameters; + QVector<HMaterial> m_handles; +}; + +typedef QSharedPointer<MaterialParameterGathererJob> MaterialParameterGathererJobPtr; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_MATERIALPARAMETERGATHERERJOB_P_H diff --git a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp new file mode 100644 index 000000000..5bfa9ed6f --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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 "renderviewcommandupdaterjob_p.h" +#include <Qt3DRender/private/job_common_p.h> +#include <renderer_p.h> +#include <renderview_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +namespace { +int renderViewInstanceCounter = 0; +} // anonymous + +RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob() + : Qt3DCore::QAspectJob() + , m_offset(0) + , m_count(0) + , m_renderView(nullptr) + , m_renderer(nullptr) + , m_renderables(nullptr) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++); +} + +void RenderViewCommandUpdaterJob::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 (m_count == 0) + return; + // Update Render Commands (Uniform Change, Depth Change) + m_renderView->updateRenderCommand(m_renderables, m_offset, m_count); + } +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h new file mode 100644 index 000000000..6a171d281 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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_RENDERVIEWCOMMANDBUILDERJOB_P_H +#define QT3DRENDER_RENDER_RENDERVIEWCOMMANDBUILDERJOB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <rendercommand_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Q_AUTOTEST_EXPORT RenderViewCommandBuilderJob : public Qt3DCore::QAspectJob +{ +public: + RenderViewCommandBuilderJob(); + + inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; } + inline void setEntities(const QVector<Entity *> &entities, int offset, int count) + { + m_offset = offset; + m_count = count; + m_entities = entities; + } + inline EntityRenderCommandData &commandData() { return m_commandData; } + + void run() final; + +private: + int m_offset; + int m_count; + RenderView *m_renderView; + QVector<Entity *> m_entities; + EntityRenderCommandData m_commandData; +}; + +typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobPtr; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEWCOMMANDBUILDERJOB_P_H diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp new file mode 100644 index 000000000..00cfba558 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderviewcommandbuilderjob_p.h" +#include <Qt3DRender/private/job_common_p.h> +#include <renderview_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +namespace { +int renderViewInstanceCounter = 0; +} // anonymous + +RenderViewCommandBuilderJob::RenderViewCommandBuilderJob() + : Qt3DCore::QAspectJob() + , m_offset(0) + , m_count(0) + , m_renderView(nullptr) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderViewCommandBuilder, renderViewInstanceCounter++) +} + +void RenderViewCommandBuilderJob::run() +{ + if (!m_renderView->noDraw()) { + if (m_count == 0) + return; + + const bool isDraw = !m_renderView->isCompute(); + if (isDraw) + m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count); + else + m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count); + } +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h new file mode 100644 index 000000000..6a171d281 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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_RENDERVIEWCOMMANDBUILDERJOB_P_H +#define QT3DRENDER_RENDER_RENDERVIEWCOMMANDBUILDERJOB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <rendercommand_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Q_AUTOTEST_EXPORT RenderViewCommandBuilderJob : public Qt3DCore::QAspectJob +{ +public: + RenderViewCommandBuilderJob(); + + inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; } + inline void setEntities(const QVector<Entity *> &entities, int offset, int count) + { + m_offset = offset; + m_count = count; + m_entities = entities; + } + inline EntityRenderCommandData &commandData() { return m_commandData; } + + void run() final; + +private: + int m_offset; + int m_count; + RenderView *m_renderView; + QVector<Entity *> m_entities; + EntityRenderCommandData m_commandData; +}; + +typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobPtr; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEWCOMMANDBUILDERJOB_P_H diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp new file mode 100644 index 000000000..1b7cae229 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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 "renderviewcommandupdaterjob_p.h" +#include <Qt3DRender/private/job_common_p.h> +#include <renderer_p.h> +#include <renderview_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +namespace { +int renderViewInstanceCounter = 0; +} // anonymous + +RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob() + : Qt3DCore::QAspectJob() + , m_offset(0) + , m_count(0) + , m_renderView(nullptr) + , m_renderer(nullptr) + , m_renderables() +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++) +} + +void RenderViewCommandUpdaterJob::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 (m_count == 0) + return; + // Update Render Commands (Uniform Change, Depth Change) + m_renderView->updateRenderCommand(m_renderables.data(), m_offset, m_count); + } +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h new file mode 100644 index 000000000..88961d848 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h @@ -0,0 +1,103 @@ +ļ»æ/**************************************************************************** +** +** 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_RENDERVIEWCOMMANDUPDATEJOB_P_H +#define QT3DRENDER_RENDER_RENDERVIEWCOMMANDUPDATEJOB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <rendercommand_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class RenderView; +class Renderer; + +class Q_AUTOTEST_EXPORT RenderViewCommandUpdaterJob : public Qt3DCore::QAspectJob +{ +public: + RenderViewCommandUpdaterJob(); + + inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; } + inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; } + inline void setRenderables(const EntityRenderCommandDataPtr &renderables, int offset, int count) Q_DECL_NOTHROW + { + m_offset = offset; + m_count = count; + m_renderables = renderables; + } + EntityRenderCommandDataPtr renderables() const { return m_renderables; } + + QVector<RenderCommand> &commands() Q_DECL_NOTHROW { return m_commands; } + + void run() final; + +private: + int m_offset; + int m_count; + RenderView *m_renderView; + Renderer *m_renderer; + EntityRenderCommandDataPtr m_renderables; + QVector<RenderCommand> m_commands; +}; + +typedef QSharedPointer<RenderViewCommandUpdaterJob> RenderViewCommandUpdaterJobPtr; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEWCOMMANDUPDATEJOB_P_H diff --git a/src/plugins/renderers/opengl/jobs/renderviewinitializerjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewinitializerjob.cpp new file mode 100644 index 000000000..c1b97959f --- /dev/null +++ b/src/plugins/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 <renderview_p.h> +#include <renderer_p.h> +#include <renderviewjobutils_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/job_common_p.h> + +#include <QElapsedTimer> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +namespace { +// 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/plugins/renderers/opengl/jobs/renderviewinitializerjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewinitializerjob_p.h new file mode 100644 index 000000000..fb4e2c67c --- /dev/null +++ b/src/plugins/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/plugins/renderers/opengl/jobs/renderviewjobutils.cpp b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp new file mode 100644 index 000000000..c454aff77 --- /dev/null +++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp @@ -0,0 +1,569 @@ +/**************************************************************************** +** +** 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 <Qt3DRender/private/renderlogging_p.h> + +#include <Qt3DRender/qgraphicsapifilter.h> +#include <Qt3DRender/private/sphere_p.h> +#include <Qt3DRender/qshaderdata.h> + +#include <Qt3DRender/private/cameraselectornode_p.h> +#include <Qt3DRender/private/clearbuffers_p.h> +#include <Qt3DRender/private/layerfilternode_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/effect_p.h> +#include <Qt3DRender/private/renderpassfilternode_p.h> +#include <Qt3DRender/private/rendertargetselectornode_p.h> +#include <Qt3DRender/private/sortpolicy_p.h> +#include <Qt3DRender/private/techniquefilternode_p.h> +#include <Qt3DRender/private/viewportnode_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/shaderdata_p.h> +#include <Qt3DRender/private/statesetnode_p.h> +#include <Qt3DRender/private/dispatchcompute_p.h> +#include <Qt3DRender/private/rendersurfaceselector_p.h> +#include <Qt3DRender/private/rendercapture_p.h> +#include <Qt3DRender/private/buffercapture_p.h> +#include <Qt3DRender/private/stringtoint_p.h> +#include <Qt3DRender/private/techniquemanager_p.h> +#include <Qt3DRender/private/memorybarrier_p.h> +#include <Qt3DRender/private/blitframebuffer_p.h> +#include <Qt3DRender/private/waitfence_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <renderview_p.h> +#include <shadervariables_p.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt3DCore; + +namespace Qt3DRender { +namespace Render { + +/*! + \internal + Walks up the framegraph tree from \a fgLeaf and builds up as much state + as possible and populates \a rv. For cases where we can't get the specific state + (e.g. because it depends upon more than just the framegraph) we store the data from + the framegraph that will be needed to later when the rest of the data becomes available +*/ +void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf) +{ + // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter + // which is referenced by the Material which is referenced by the RenderMesh. So we can + // only store the filter info in the RenderView structure and use it to do the resolving + // when we build the RenderCommand list. + const NodeManagers *manager = rv->nodeManagers(); + const FrameGraphNode *node = fgLeaf; + + while (node) { + FrameGraphNode::FrameGraphNodeType type = node->nodeType(); + if (node->isEnabled()) + switch (type) { + case FrameGraphNode::InvalidNodeType: + // A base FrameGraphNode, can be used for grouping purposes + break; + case FrameGraphNode::CameraSelector: + // Can be set only once and we take camera nearest to the leaf node + if (!rv->renderCameraLens()) { + const CameraSelector *cameraSelector = static_cast<const CameraSelector *>(node); + Entity *camNode = manager->renderNodesManager()->lookupResource(cameraSelector->cameraUuid()); + if (camNode) { + CameraLens *lens = camNode->renderComponent<CameraLens>(); + rv->setRenderCameraEntity(camNode); + if (lens && lens->isEnabled()) { + rv->setRenderCameraLens(lens); + // ViewMatrix and ProjectionMatrix are computed + // later in updateMatrices() + // since at this point the transformation matrices + // may not yet have been updated + } + } + } + break; + + case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree + rv->appendLayerFilter(static_cast<const LayerFilterNode *>(node)->peerId()); + break; + + case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree + rv->appendProximityFilterId(node->peerId()); + break; + + case FrameGraphNode::RenderPassFilter: + // Can be set once + // TODO: Amalgamate all render pass filters from leaf to root + if (!rv->renderPassFilter()) + rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node)); + break; + + case FrameGraphNode::RenderTarget: { + // Can be set once and we take render target nearest to the leaf node + const RenderTargetSelector *targetSelector = static_cast<const RenderTargetSelector *>(node); + QNodeId renderTargetUid = targetSelector->renderTargetUuid(); + HTarget renderTargetHandle = manager->renderTargetManager()->lookupHandle(renderTargetUid); + + // Add renderTarget Handle and build renderCommand AttachmentPack + if (!rv->renderTargetId()) { + rv->setRenderTargetId(renderTargetUid); + + RenderTarget *renderTarget = manager->renderTargetManager()->data(renderTargetHandle); + if (renderTarget) + rv->setAttachmentPack(AttachmentPack(renderTarget, manager->attachmentManager(), targetSelector->outputs())); + } + break; + } + + case FrameGraphNode::ClearBuffers: { + const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node); + rv->addClearBuffers(cbNode); + break; + } + + case FrameGraphNode::TechniqueFilter: + // Can be set once + // TODO Amalgamate all technique filters from leaf to root + if (!rv->techniqueFilter()) + rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node)); + break; + + case FrameGraphNode::Viewport: { + // If the Viewport has already been set in a lower node + // Make it so that the new viewport is actually + // a subregion relative to that of the parent viewport + const ViewportNode *vpNode = static_cast<const ViewportNode *>(node); + rv->setViewport(ViewportNode::computeViewport(rv->viewport(), vpNode)); + rv->setGamma(vpNode->gamma()); + break; + } + + case FrameGraphNode::SortMethod: { + const Render::SortPolicy *sortPolicy = static_cast<const Render::SortPolicy *>(node); + rv->addSortType(sortPolicy->sortTypes()); + break; + } + + case FrameGraphNode::SubtreeEnabler: + // Has no meaning here. SubtreeEnabler was used + // in a prior step to filter the list of RenderViewJobs + break; + + case FrameGraphNode::StateSet: { + const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node); + // Create global RenderStateSet for renderView if no stateSet was set before + RenderStateSet *stateSet = rv->stateSet(); + if (stateSet == nullptr && rStateSet->hasRenderStates()) { + stateSet = new RenderStateSet(); + rv->setStateSet(stateSet); + } + + // Add states from new stateSet we might be missing + // but don' t override existing states (lower StateSetNode always has priority) + if (rStateSet->hasRenderStates()) + addStatesToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager()); + break; + } + + case FrameGraphNode::NoDraw: { + rv->setNoDraw(true); + break; + } + + case FrameGraphNode::FrustumCulling: { + rv->setFrustumCulling(true); + break; + } + + case FrameGraphNode::ComputeDispatch: { + const Render::DispatchCompute *dispatchCompute = static_cast<const Render::DispatchCompute *>(node); + rv->setCompute(true); + rv->setComputeWorkgroups(dispatchCompute->x(), + dispatchCompute->y(), + dispatchCompute->z()); + break; + } + + case FrameGraphNode::Lighting: { + // TODO + break; + } + + case FrameGraphNode::Surface: { + // Use the surface closest to leaf node + if (rv->surface() == nullptr) { + const Render::RenderSurfaceSelector *surfaceSelector + = static_cast<const Render::RenderSurfaceSelector *>(node); + rv->setSurface(surfaceSelector->surface()); + rv->setSurfaceSize(surfaceSelector->renderTargetSize() * surfaceSelector->devicePixelRatio()); + rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio()); + } + break; + } + case FrameGraphNode::RenderCapture: { + auto *renderCapture = const_cast<Render::RenderCapture *>( + static_cast<const Render::RenderCapture *>(node)); + if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) { + rv->setRenderCaptureNodeId(renderCapture->peerId()); + rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest()); + } + break; + } + + case FrameGraphNode::MemoryBarrier: { + 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; + } + + case FrameGraphNode::WaitFence: { + const Render::WaitFence *waitFence = static_cast<const Render::WaitFence *>(node); + rv->appendWaitFence(waitFence->data()); + break; + } + + case FrameGraphNode::SetFence: { + rv->appendInsertFenceId(node->peerId()); + break; + } + + case FrameGraphNode::NoPicking: + // Nothing to do RenderView wise for NoPicking + break; + + default: + // Should never get here + qCWarning(Backend) << "Unhandled FrameGraphNode type"; + } + + node = node->parent(); + } +} + +/*! + \internal + Searches the best matching Technique from \a effect specified. +*/ +Technique *findTechniqueForEffect(NodeManagers *manager, + const TechniqueFilter *techniqueFilter, + Effect *effect) +{ + if (!effect) + return nullptr; + + QVector<Technique*> matchingTechniques; + const bool hasInvalidTechniqueFilter = (techniqueFilter == nullptr || techniqueFilter->filters().isEmpty()); + + // Iterate through the techniques in the effect + const auto techniqueIds = effect->techniques(); + for (const QNodeId techniqueId : techniqueIds) { + Technique *technique = manager->techniqueManager()->lookupResource(techniqueId); + + // Should be valid, if not there likely a problem with node addition/destruction changes + Q_ASSERT(technique); + + // Check if the technique is compatible with the rendering API + // If no techniqueFilter is present, we return the technique as it satisfies OpenGL version + if (technique->isCompatibleWithRenderer() && (hasInvalidTechniqueFilter || technique->isCompatibleWithFilters(techniqueFilter->filters()))) + matchingTechniques.append(technique); + } + + if (matchingTechniques.size() == 0) // We failed to find a suitable technique to use :( + return nullptr; + + if (matchingTechniques.size() == 1) + return matchingTechniques.first(); + + // Several compatible techniques, return technique with highest major and minor version + Technique* highest = matchingTechniques.first(); + GraphicsApiFilterData filter = *highest->graphicsApiFilter(); + for (auto it = matchingTechniques.cbegin() + 1; it < matchingTechniques.cend(); ++it) { + if (filter < *(*it)->graphicsApiFilter()) { + filter = *(*it)->graphicsApiFilter(); + highest = *it; + } + } + return highest; +} + + +RenderPassList findRenderPassesForTechnique(NodeManagers *manager, + const RenderPassFilter *passFilter, + Technique *technique) +{ + Q_ASSERT(manager); + Q_ASSERT(technique); + + RenderPassList passes; + const auto passIds = technique->renderPasses(); + for (const QNodeId passId : passIds) { + RenderPass *renderPass = manager->renderPassManager()->lookupResource(passId); + + if (renderPass && renderPass->isEnabled()) { + bool foundMatch = (!passFilter || passFilter->filters().size() == 0); + + // A pass filter is present so we need to check for matching criteria + if (!foundMatch && renderPass->filterKeys().size() >= passFilter->filters().size()) { + + // Iterate through the filter criteria and look for render passes with criteria that satisfy them + const auto filterKeyIds = passFilter->filters(); + for (const QNodeId filterKeyId : filterKeyIds) { + foundMatch = false; + FilterKey *filterFilterKey = manager->filterKeyManager()->lookupResource(filterKeyId); + + const auto passFilterKeyIds = renderPass->filterKeys(); + for (const QNodeId passFilterKeyId : passFilterKeyIds) { + FilterKey *passFilterKey = manager->filterKeyManager()->lookupResource(passFilterKeyId); + if ((foundMatch = (*passFilterKey == *filterFilterKey))) + break; + } + + if (!foundMatch) { + // No match for criterion in any of the render pass' criteria + break; + } + } + } + + if (foundMatch) { + // Found a renderpass that satisfies our needs. Add it in order + passes << renderPass; + } + } + } + + return passes; +} + + +ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *params, const int nameId) +{ + const ParameterInfoList::const_iterator end = params->cend(); + ParameterInfoList::const_iterator it = std::lower_bound(params->cbegin(), end, nameId); + if (it != end && it->nameId != nameId) + return end; + return it; +} + +void addParametersForIds(ParameterInfoList *params, ParameterManager *manager, + const Qt3DCore::QNodeIdVector ¶meterIds) +{ + for (const QNodeId paramId : parameterIds) { + const HParameter parameterHandle = manager->lookupHandle(paramId); + const Parameter *param = manager->data(parameterHandle); + ParameterInfoList::iterator it = std::lower_bound(params->begin(), params->end(), param->nameId()); + if (it == params->end() || it->nameId != param->nameId()) + params->insert(it, ParameterInfo(param->nameId(), parameterHandle)); + } +} + +void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList, + ParameterManager *manager, + Material *material, + Effect *effect, + Technique *technique) +{ + // The parameters are taken in the following priority order: + // + // 1) Material + // 2) Effect + // 3) Technique + // + // That way a user can override defaults in Effect's and Techniques on a + // object manner and a Technique can override global defaults from the Effect. + parametersFromParametersProvider(infoList, manager, material); + parametersFromParametersProvider(infoList, manager, effect); + parametersFromParametersProvider(infoList, manager, technique); +} + +// Only add states with types we don't already have +void addStatesToRenderStateSet(RenderStateSet *stateSet, + const QVector<Qt3DCore::QNodeId> stateIds, + RenderStateManager *manager) +{ + for (const Qt3DCore::QNodeId &stateId : stateIds) { + RenderStateNode *node = manager->lookupResource(stateId); + if (node->isEnabled() && stateSet->canAddStateOfType(node->type())) { + stateSet->addState(node->impl()); + } + } +} + +namespace { + +const QString blockArray = QStringLiteral("[%1]"); +const int qNodeIdTypeId = qMetaTypeId<QNodeId>(); + +} + +UniformBlockValueBuilder::UniformBlockValueBuilder() + : updatedPropertiesOnly(false) + , shaderDataManager(nullptr) + , textureManager(nullptr) +{ +} + +UniformBlockValueBuilder::~UniformBlockValueBuilder() +{ +} + +void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData, const QString &blockName, const QString &qmlPropertyName, const QVariant &value) +{ + // In the end, values are either scalar or a scalar array + // Composed elements (structs, structs array) are simplified into simple scalars + if (value.userType() == QMetaType::QVariantList) { // Array + QVariantList list = value.value<QVariantList>(); + if (list.at(0).userType() == qNodeIdTypeId) { // Array of struct qmlPropertyName[i].structMember + for (int i = 0; i < list.size(); ++i) { + const QVariant variantElement = list.at(i); + if (list.at(i).userType() == qNodeIdTypeId) { + const auto nodeId = variantElement.value<QNodeId>(); + ShaderData *subShaderData = shaderDataManager->lookupResource(nodeId); + if (subShaderData) { + buildActiveUniformNameValueMapStructHelper(subShaderData, + blockName + QLatin1Char('.') + qmlPropertyName + blockArray.arg(i), + QLatin1String("")); + } + // Note: we only handle ShaderData as nested container nodes here + } + } + } else { // Array of scalar/vec qmlPropertyName[0] + QString varName; + varName.reserve(blockName.length() + 1 + qmlPropertyName.length() + 3); + varName.append(blockName); + varName.append(QLatin1String(".")); + varName.append(qmlPropertyName); + varName.append(QLatin1String("[0]")); + if (uniforms.contains(varName)) { + qCDebug(Shaders) << "UBO array member " << varName << " set for update"; + activeUniformNamesToValue.insert(StringToInt::lookupId(varName), value); + } + } + } else if (value.userType() == qNodeIdTypeId) { // Struct qmlPropertyName.structMember + const auto nodeId = value.value<QNodeId>(); + ShaderData *rSubShaderData = shaderDataManager->lookupResource(nodeId); + if (rSubShaderData) { + buildActiveUniformNameValueMapStructHelper(rSubShaderData, + blockName, + qmlPropertyName); + } else if (textureManager->contains(nodeId)) { + const auto varId = StringToInt::lookupId(blockName + QLatin1Char('.') + qmlPropertyName); + activeUniformNamesToValue.insert(varId, value); + } + } else { // Scalar / Vec + QString varName; + varName.reserve(blockName.length() + 1 + qmlPropertyName.length()); + varName.append(blockName); + varName.append(QLatin1String(".")); + varName.append(qmlPropertyName); + if (uniforms.contains(varName)) { + qCDebug(Shaders) << "UBO scalar member " << varName << " set for update"; + + // If the property needs to be transformed, we transform it here as + // the shaderdata cannot hold transformed properties for multiple + // thread contexts at once + activeUniformNamesToValue.insert(StringToInt::lookupId(varName), + currentShaderData->getTransformedProperty(qmlPropertyName, viewMatrix)); + } + } +} + +void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName) +{ + const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties(); + auto it = properties.begin(); + const auto end = properties.end(); + + while (it != end) { + QString fullBlockName; + fullBlockName.reserve(blockName.length() + 1 + qmlPropertyName.length()); + fullBlockName.append(blockName); + if (!qmlPropertyName.isEmpty()) { + fullBlockName.append(QLatin1String(".")); + fullBlockName.append(qmlPropertyName); + } + buildActiveUniformNameValueMapHelper(rShaderData, fullBlockName, + it.key(), it.value().value); + ++it; + } +} + +ParameterInfo::ParameterInfo(const int nameId, const HParameter &handle) + : nameId(nameId) + , handle(handle) +{} + +bool ParameterInfo::operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT +{ + return nameId < other.nameId; +} + +bool ParameterInfo::operator<(const int otherNameId) const Q_DECL_NOEXCEPT +{ + return nameId < otherNameId; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h b/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h new file mode 100644 index 000000000..b2fa59785 --- /dev/null +++ b/src/plugins/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(NodeManagers *manager, + const TechniqueFilter *techniqueFilter, + Effect *effect); + +typedef QVarLengthArray<RenderPass*, 4> RenderPassList; +Q_AUTOTEST_EXPORT RenderPassList findRenderPassesForTechnique(NodeManagers *manager, + const RenderPassFilter *passFilter, + Technique *technique); + +// Extracts the type T from a QVariant v without using QVariant::value which is slow +// Note: Assumes you are 100% sure about the type you requested +template<typename T> +inline T variant_value(const QVariant &v) +{ + return *reinterpret_cast<const T *>(v.data()); +} + +struct ParameterInfo +{ + explicit ParameterInfo(const int nameId = -1, const HParameter &handle = HParameter()); + + int nameId; + HParameter handle; + + bool operator<(const int otherNameId) const Q_DECL_NOEXCEPT; + bool operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT; +}; +typedef QVector<ParameterInfo> ParameterInfoList; + +struct RenderPassParameterData +{ + RenderPass *pass; + ParameterInfoList parameterInfo; +}; +QT3D_DECLARE_TYPEINFO_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> ¶meterIds); + +template<class T> +void parametersFromParametersProvider(ParameterInfoList *infoList, + ParameterManager *manager, + T *provider) +{ + addParametersForIds(infoList, manager, provider->parameters()); +} + +Q_AUTOTEST_EXPORT ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *infoList, + const int nameId); + +Q_AUTOTEST_EXPORT void addStatesToRenderStateSet(RenderStateSet *stateSet, + const QVector<Qt3DCore::QNodeId> stateIds, + RenderStateManager *manager); + +typedef QHash<int, QVariant> UniformBlockValueBuilderHash; + +struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder +{ + UniformBlockValueBuilder(); + ~UniformBlockValueBuilder(); + + QT3D_ALIGNED_MALLOC_AND_FREE() + + void buildActiveUniformNameValueMapHelper(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/plugins/renderers/opengl/main.cpp b/src/plugins/renderers/opengl/main.cpp new file mode 100644 index 000000000..61e796788 --- /dev/null +++ b/src/plugins/renderers/opengl/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <Qt3DRender/private/qrendererplugin_p.h> +#include <renderer_p.h> + +QT_BEGIN_NAMESPACE + +class OpenGLRendererPlugin : public Qt3DRender::Render::QRendererPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QRendererPluginFactoryInterface_iid FILE "openglrenderer.json") + + Qt3DRender::Render::AbstractRenderer *create(const QString &key, Qt3DRender::QRenderAspect::RenderType renderMode) override + { + Q_UNUSED(key) + return new Qt3DRender::Render::Renderer(renderMode); + } +}; + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/renderers/opengl/managers/gl_handle_types_p.h b/src/plugins/renderers/opengl/managers/gl_handle_types_p.h new file mode 100644 index 000000000..eb25bab7f --- /dev/null +++ b/src/plugins/renderers/opengl/managers/gl_handle_types_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_OPENGL_HANDLE_TYPES_P_H +#define QT3DRENDER_RENDER_OPENGL_HANDLE_TYPES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/private/qhandle_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class GLBuffer; +class GLTexture; +class OpenGLVertexArrayObject; + +typedef Qt3DCore::QHandle<GLBuffer> HGLBuffer; +typedef Qt3DCore::QHandle<OpenGLVertexArrayObject> HVao; +typedef Qt3DCore::QHandle<GLTexture> HGLTexture; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_OPENGL_HANDLE_TYPES_P_H diff --git a/src/plugins/renderers/opengl/managers/glresourcemanagers.cpp b/src/plugins/renderers/opengl/managers/glresourcemanagers.cpp new file mode 100644 index 000000000..67a7422aa --- /dev/null +++ b/src/plugins/renderers/opengl/managers/glresourcemanagers.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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 "glresourcemanagers_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + + +GLResourceManagers::GLResourceManagers() + : m_glBufferManager(new GLBufferManager()) + , m_glShaderManager(new GLShaderManager()) + , m_glTextureManager(new GLTextureManager()) + , m_glFenceManager(new GLFenceManager()) + , m_vaoManager(new VAOManager()) +{ +} + +GLResourceManagers::~GLResourceManagers() +{ + delete m_vaoManager; + delete m_glFenceManager; + delete m_glTextureManager; + delete m_glShaderManager; + delete m_glBufferManager; +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/managers/glresourcemanagers_p.h b/src/plugins/renderers/opengl/managers/glresourcemanagers_p.h new file mode 100644 index 000000000..3597e6965 --- /dev/null +++ b/src/plugins/renderers/opengl/managers/glresourcemanagers_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** 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_GLRESOURCEMANAGERS_P_H +#define QT3DRENDER_RENDER_GLRESOURCEMANAGERS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/qt3drender_global_p.h> +#include <Qt3DCore/private/qresourcemanager_p.h> +#include <gltexture_p.h> +#include <glbuffer_p.h> +#include <glfence_p.h> +#include <openglvertexarrayobject_p.h> +#include <glshader_p.h> +#include <Qt3DRender/private/apishadermanager_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Q_AUTOTEST_EXPORT GLFenceManager : public QHash<Qt3DCore::QNodeId, GLFence> +{ +}; + +class Q_AUTOTEST_EXPORT VAOManager : public Qt3DCore::QResourceManager< + OpenGLVertexArrayObject, + VAOIdentifier, + Qt3DCore::NonLockingPolicy> +{ +public: + VAOManager() {} +}; + +class Q_AUTOTEST_EXPORT GLBufferManager : public Qt3DCore::QResourceManager< + GLBuffer, + Qt3DCore::QNodeId, + Qt3DCore::NonLockingPolicy> +{ +}; + +class Q_AUTOTEST_EXPORT GLTextureManager : public Qt3DCore::QResourceManager< + GLTexture, + Qt3DCore::QNodeId, + Qt3DCore::NonLockingPolicy> +{ +public: + QHash<GLTexture *, Qt3DCore::QNodeId> texNodeIdForGLTexture; +}; + +class Q_AUTOTEST_EXPORT GLShaderManager : public APIShaderManager<GLShader> +{ +public: + explicit GLShaderManager() + : APIShaderManager<GLShader>() + {} +}; + + +class Q_AUTOTEST_EXPORT GLResourceManagers +{ +public: + GLResourceManagers(); + ~GLResourceManagers(); + + inline VAOManager *vaoManager() const noexcept { return m_vaoManager; } + inline GLShaderManager *glShaderManager() const noexcept { return m_glShaderManager; } + inline GLTextureManager *glTextureManager() const noexcept { return m_glTextureManager; } + inline GLBufferManager *glBufferManager() const noexcept { return m_glBufferManager; } + inline GLFenceManager *glFenceManager() const noexcept { return m_glFenceManager; } + +private: + GLBufferManager *m_glBufferManager; + GLShaderManager *m_glShaderManager; + GLTextureManager *m_glTextureManager; + GLFenceManager *m_glFenceManager; + VAOManager *m_vaoManager; +}; + +} // Render + +} // Qt3DRender + +Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::OpenGLVertexArrayObject, Q_REQUIRES_CLEANUP) + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLRESOURCEMANAGERS_P_H diff --git a/src/plugins/renderers/opengl/managers/managers.pri b/src/plugins/renderers/opengl/managers/managers.pri new file mode 100644 index 000000000..97a4c2c45 --- /dev/null +++ b/src/plugins/renderers/opengl/managers/managers.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/gl_handle_types_p.h \ + $$PWD/glresourcemanagers_p.h + +SOURCES += \ + $$PWD/glresourcemanagers.cpp diff --git a/src/plugins/renderers/opengl/opengl.pri b/src/plugins/renderers/opengl/opengl.pri new file mode 100644 index 000000000..8ae4a11af --- /dev/null +++ b/src/plugins/renderers/opengl/opengl.pri @@ -0,0 +1,17 @@ + +include (renderer/renderer.pri) +include (jobs/jobs.pri) +include (io/io.pri) +include (textures/textures.pri) +include (graphicshelpers/graphicshelpers.pri) +include (managers/managers.pri) + +INCLUDEPATH += $$PWD + +# Qt3D is free of Q_FOREACH - make sure it stays that way: +DEFINES += QT_NO_FOREACH + +gcov { + QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage + QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage +} diff --git a/src/plugins/renderers/opengl/opengl.pro b/src/plugins/renderers/opengl/opengl.pro new file mode 100644 index 000000000..c04503ec1 --- /dev/null +++ b/src/plugins/renderers/opengl/opengl.pro @@ -0,0 +1,28 @@ +TARGET = openglrenderer +QT += core-private gui-private 3dcore 3dcore-private 3drender 3drender-private +QT_PRIVATE = openglextensions + +# Qt3D is free of Q_FOREACH - make sure it stays that way: +DEFINES += QT_NO_FOREACH + +SOURCES += \ + main.cpp + +DISTFILES += \ + openglrenderer.json + +include(opengl.pri) + +qtConfig(qt3d-simd-avx2) { + CONFIG += simd + QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_AVX2 +} + +qtConfig(qt3d-simd-sse2):!qtConfig(qt3d-simd-avx2) { + CONFIG += simd + QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_SSE2 +} + +PLUGIN_TYPE = renderers +PLUGIN_CLASS_NAME = OpenGLRendererPlugin +load(qt_plugin) diff --git a/src/plugins/renderers/opengl/openglrenderer.json b/src/plugins/renderers/opengl/openglrenderer.json new file mode 100644 index 000000000..6cafa2ffa --- /dev/null +++ b/src/plugins/renderers/opengl/openglrenderer.json @@ -0,0 +1,3 @@ +{ + "Keys": ["opengl"] +} diff --git a/src/plugins/renderers/opengl/renderer/commandexecuter.cpp b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp new file mode 100644 index 000000000..6498b44b8 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp @@ -0,0 +1,391 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandexecuter_p.h" + +#include <Qt3DCore/private/qabstractaspect_p.h> +#include <Qt3DCore/qbackendnode.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/geometryrenderermanager_p.h> +#include <Qt3DRender/private/stringtoint_p.h> +#include <QJsonObject> +#include <QJsonDocument> +#include <QJsonArray> +#include <graphicscontext_p.h> +#include <renderview_p.h> +#include <rendercommand_p.h> +#include <renderer_p.h> +#include <submissioncontext_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Debug { + +namespace { + +template<typename Type> +QJsonObject typeToJsonObj(const Type &) +{ + return QJsonObject(); +} + +template<typename Type> +QJsonValue typeToJsonValue(const Type &t) +{ + Q_UNUSED(t); + return QJsonValue(); +} + +template<> +QJsonObject typeToJsonObj<QRectF>(const QRectF &rect) +{ + QJsonObject obj; + + obj.insert(QLatin1String("x"), rect.x()); + obj.insert(QLatin1String("y"), rect.y()); + obj.insert(QLatin1String("width"), rect.width()); + obj.insert(QLatin1String("height"), rect.height()); + + return obj; +} + +template<> +QJsonValue typeToJsonValue<QRectF>(const QRectF &rect) +{ + QJsonArray value; + + value.push_back(rect.x()); + value.push_back(rect.y()); + value.push_back(rect.width()); + value.push_back(rect.height()); + + return value; +} + +template<> +QJsonObject typeToJsonObj<QSize>(const QSize &s) +{ + QJsonObject obj; + + obj.insert(QLatin1String("width"), s.width()); + obj.insert(QLatin1String("height"), s.height()); + + return obj; +} + +template<> +QJsonValue typeToJsonValue<QSize>(const QSize &s) +{ + QJsonArray value; + + value.push_back(s.width()); + value.push_back(s.height()); + + return value; +} + +template<> +QJsonObject typeToJsonObj<QVector3D>(const QVector3D &v) +{ + QJsonObject obj; + + obj.insert(QLatin1String("x"), v.x()); + obj.insert(QLatin1String("y"), v.y()); + obj.insert(QLatin1String("z"), v.z()); + + return obj; +} + +template<> +QJsonValue typeToJsonValue<QVector3D>(const QVector3D &v) +{ + QJsonArray value; + + value.push_back(v.x()); + value.push_back(v.y()); + value.push_back(v.z()); + + return value; +} + +template<> +QJsonObject typeToJsonObj<Qt3DCore::QNodeId>(const Qt3DCore::QNodeId &v) +{ + QJsonObject obj; + obj.insert(QLatin1String("id"), qint64(v.id())); + return obj; +} + +template<> +QJsonValue typeToJsonValue<Qt3DCore::QNodeId>(const Qt3DCore::QNodeId &v) +{ + QJsonValue value(qint64(v.id())); + return value; +} + +template<> +QJsonObject typeToJsonObj<QVector4D>(const QVector4D &v) +{ + QJsonObject obj; + + obj.insert(QLatin1String("x"), v.x()); + obj.insert(QLatin1String("y"), v.y()); + obj.insert(QLatin1String("z"), v.z()); + obj.insert(QLatin1String("w"), v.w()); + + return obj; +} + +template<> +QJsonValue typeToJsonValue<QVector4D>(const QVector4D &v) +{ + QJsonArray value; + + value.push_back(v.x()); + value.push_back(v.y()); + value.push_back(v.z()); + value.push_back(v.w()); + + return value; +} + +template<> +QJsonObject typeToJsonObj<QMatrix4x4>(const QMatrix4x4 &v) +{ + QJsonObject obj; + + obj.insert(QLatin1String("row 0"), typeToJsonObj(v.row(0))); + obj.insert(QLatin1String("row 1"), typeToJsonObj(v.row(0))); + obj.insert(QLatin1String("row 2"), typeToJsonObj(v.row(0))); + obj.insert(QLatin1String("row 3"), typeToJsonObj(v.row(0))); + + return obj; +} + +template<> +QJsonValue typeToJsonValue<QMatrix4x4>(const QMatrix4x4 &v) +{ + QJsonArray value; + + value.push_back(typeToJsonValue(v.row(0))); + value.push_back(typeToJsonValue(v.row(1))); + value.push_back(typeToJsonValue(v.row(2))); + value.push_back(typeToJsonValue(v.row(3))); + + return value; +} + +template<> +QJsonValue typeToJsonValue<QVariant>(const QVariant &v) +{ + const int nodeTypeId = qMetaTypeId<Qt3DCore::QNodeId>(); + + if (v.userType() == nodeTypeId) + return typeToJsonValue(v.value<Qt3DCore::QNodeId>()); + + switch (v.userType()) { + case QMetaType::QVector3D: + return typeToJsonValue(v.value<QVector3D>()); + case QMetaType::QVector4D: + return typeToJsonValue(v.value<QVector4D>()); + case QMetaType::QMatrix4x4: + return typeToJsonValue(v.value<QMatrix4x4>()); + default: + return QJsonValue::fromVariant(v); + } +} + +template<typename Handle, typename Manager> +QJsonObject backendNodeToJSon(Handle handle, Manager *manager) +{ + Qt3DCore::QBackendNode *node = manager->data(handle); + QJsonObject obj; + Qt3DCore::QNodeId id; + if (node != nullptr) + id = node->peerId(); + obj.insert(QLatin1String("id"), qint64(id.id())); + return obj; +} + +QJsonObject parameterPackToJson(const Render::ShaderParameterPack &pack) +{ + QJsonObject obj; + + const Render::PackUniformHash &uniforms = pack.uniforms(); + QJsonArray uniformsArray; + for (int i = 0, m = uniforms.keys.size(); i < m; ++i) { + QJsonObject uniformObj; + uniformObj.insert(QLatin1String("name"), Render::StringToInt::lookupString(uniforms.keys.at(i))); + const Render::UniformValue::ValueType type = uniforms.values.at(i).valueType(); + uniformObj.insert(QLatin1String("type"), + type == Render::UniformValue::ScalarValue + ? QLatin1String("value") + : QLatin1String("texture")); + uniformsArray.push_back(uniformObj); + } + obj.insert(QLatin1String("uniforms"), uniformsArray); + + QJsonArray texturesArray; + const QVector<Render::ShaderParameterPack::NamedResource> &textures = pack.textures(); + for (const auto & texture : textures) { + QJsonObject textureObj; + textureObj.insert(QLatin1String("name"), Render::StringToInt::lookupString(texture.glslNameId)); + textureObj.insert(QLatin1String("id"), qint64(texture.nodeId.id())); + texturesArray.push_back(textureObj); + } + obj.insert(QLatin1String("textures"), texturesArray); + + const QVector<Render::BlockToUBO> &ubos = pack.uniformBuffers(); + QJsonArray ubosArray; + for (const auto &ubo : ubos) { + QJsonObject uboObj; + uboObj.insert(QLatin1String("index"), ubo.m_blockIndex); + uboObj.insert(QLatin1String("bufferId"), qint64(ubo.m_bufferID.id())); + ubosArray.push_back(uboObj); + + } + obj.insert(QLatin1String("ubos"), ubosArray); + + const QVector<Render::BlockToSSBO> &ssbos = pack.shaderStorageBuffers(); + QJsonArray ssbosArray; + for (const auto &ssbo : ssbos) { + QJsonObject ssboObj; + ssboObj.insert(QLatin1String("index"), ssbo.m_blockIndex); + ssboObj.insert(QLatin1String("bufferId"), qint64(ssbo.m_bufferID.id())); + ssbosArray.push_back(ssboObj); + } + obj.insert(QLatin1String("ssbos"), ssbosArray); + + return obj; +} + +} // anonymous + +CommandExecuter::CommandExecuter(Render::Renderer *renderer) + : m_renderer(renderer) +{ +} + +// Render thread +void CommandExecuter::performAsynchronousCommandExecution(const QVector<Render::RenderView *> &views) +{ + QMutexLocker lock(&m_pendingCommandsMutex); + const QVector<Qt3DCore::Debug::AsynchronousCommandReply *> shellCommands = std::move(m_pendingCommands); + lock.unlock(); + + for (auto *reply : shellCommands) { + if (reply->commandName() == QLatin1String("glinfo")) { + QJsonObject replyObj; + const GraphicsApiFilterData *contextInfo = m_renderer->submissionContext()->contextInfo(); + if (contextInfo != nullptr) { + replyObj.insert(QLatin1String("api"), + contextInfo->m_api == QGraphicsApiFilter::OpenGL + ? QLatin1String("OpenGL") + : QLatin1String("OpenGLES")); + const QString versionString = + QString::number(contextInfo->m_major) + + QStringLiteral(".") + + QString::number(contextInfo->m_minor); + replyObj.insert(QLatin1String("version"), versionString); + replyObj.insert(QLatin1String("profile"), + contextInfo->m_profile == QGraphicsApiFilter::CoreProfile + ? QLatin1String("Core") + : contextInfo->m_profile == QGraphicsApiFilter::CompatibilityProfile + ? QLatin1String("Compatibility") + : QLatin1String("None")); + } + reply->setData(QJsonDocument(replyObj).toJson()); + } else if (reply->commandName() == QLatin1String("rendercommands")) { + QJsonObject replyObj; + + QJsonArray viewArray; + for (Render::RenderView *v : views) { + QJsonObject viewObj; + viewObj.insert(QLatin1String("viewport"), typeToJsonValue(v->viewport())); + viewObj.insert(QLatin1String("surfaceSize"), typeToJsonValue(v->surfaceSize())); + viewObj.insert(QLatin1String("devicePixelRatio"), v->devicePixelRatio()); + viewObj.insert(QLatin1String("noDraw"), v->noDraw()); + viewObj.insert(QLatin1String("frustumCulling"), v->frustumCulling()); + viewObj.insert(QLatin1String("compute"), v->isCompute()); + viewObj.insert(QLatin1String("clearDepthValue"), v->clearDepthValue()); + viewObj.insert(QLatin1String("clearStencilValue"), v->clearStencilValue()); + + QJsonArray renderCommandsArray; + for (const Render::RenderCommand &c : v->commands()) { + QJsonObject commandObj; + Render::NodeManagers *nodeManagers = m_renderer->nodeManagers(); + commandObj.insert(QLatin1String("shader"), typeToJsonValue(QVariant::fromValue(c.m_shaderId))); + commandObj.insert(QLatin1String("vao"), double(c.m_vao.handle())); + commandObj.insert(QLatin1String("instanceCount"), c.m_instanceCount); + commandObj.insert(QLatin1String("geometry"), backendNodeToJSon(c.m_geometry, nodeManagers->geometryManager())); + commandObj.insert(QLatin1String("geometryRenderer"), backendNodeToJSon(c.m_geometryRenderer, nodeManagers->geometryRendererManager())); + commandObj.insert(QLatin1String("shaderParameterPack"), parameterPackToJson(c.m_parameterPack)); + + renderCommandsArray.push_back(commandObj); + } + viewObj.insert(QLatin1String("commands"), renderCommandsArray); + viewArray.push_back(viewObj); + } + + replyObj.insert(QLatin1String("renderViews"), viewArray); + reply->setData(QJsonDocument(replyObj).toJson()); + } + reply->setFinished(true); + } +} + +// Main thread +QVariant CommandExecuter::executeCommand(const QStringList &args) +{ + // Note: The replies will be deleted by the AspectCommandDebugger + if (args.length() > 0 && + (args.first() == QLatin1String("glinfo") || + args.first() == QLatin1String("rendercommands"))) { + auto reply = new Qt3DCore::Debug::AsynchronousCommandReply(args.first()); + QMutexLocker lock(&m_pendingCommandsMutex); + m_pendingCommands.push_back(reply); + return QVariant::fromValue(reply); + } + return QVariant(); +} + +} // Debug + +} // Qt3DRenderer + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/commandexecuter_p.h b/src/plugins/renderers/opengl/renderer/commandexecuter_p.h new file mode 100644 index 000000000..2d90bf4d6 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/commandexecuter_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_DEBUG_COMMANDEXECUTER_H +#define QT3DRENDER_DEBUG_COMMANDEXECUTER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QVector> +#include <QVariant> +#include <QMutex> + +QT_BEGIN_NAMESPACE + +namespace Qt3DCore { + +namespace Debug { +class AsynchronousCommandReply; +} // Debug + +} // Qt3DCore + +namespace Qt3DRender { + +namespace Render { +class Renderer; +class RenderView; +} // Render + +namespace Debug { + +class CommandExecuter +{ +public: + explicit CommandExecuter(Render::Renderer *renderer); + + void performAsynchronousCommandExecution(const QVector<Render::RenderView *> &views); + + QVariant executeCommand(const QStringList &args); + +private: + Render::Renderer *m_renderer; + QVector<Qt3DCore::Debug::AsynchronousCommandReply *> m_pendingCommands; + QMutex m_pendingCommandsMutex; +}; + +} // Debug + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_DEBUG_COMMANDEXECUTER_H diff --git a/src/plugins/renderers/opengl/renderer/glfence_p.h b/src/plugins/renderers/opengl/renderer/glfence_p.h new file mode 100644 index 000000000..366065048 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/glfence_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GLFENCE_P_H +#define GLFENCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGlobal> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +// GLsync is a pointer to a struct (unlike the rest of GL which used int ids) +// We cannot reference GLsync as it's only available since 3.2 We use FenceId +// to wrap that around and trust the GLHelpers will convert them accordingly. +using GLFence = void *; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + + +#endif // GLFENCE_P_H diff --git a/src/plugins/renderers/opengl/renderer/glshader.cpp b/src/plugins/renderers/opengl/renderer/glshader.cpp new file mode 100644 index 000000000..02fa2de6d --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/glshader.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "glshader_p.h" +#include <QMutexLocker> +#include <Qt3DRender/private/stringtoint_p.h> +#include <graphicscontext_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +GLShader::GLShader() + : m_isLoaded(false) + , m_graphicsContext(nullptr) +{ + m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1); +} + +void GLShader::setGraphicsContext(GraphicsContext *context) +{ + QMutexLocker lock(&m_mutex); + m_graphicsContext = context; + if (m_graphicsContext) { + m_contextConnection = QObject::connect(m_graphicsContext->openGLContext(), + &QOpenGLContext::aboutToBeDestroyed, + [this] { setGraphicsContext(nullptr); }); + } +} + +GraphicsContext *GLShader::graphicsContext() +{ + QMutexLocker lock(&m_mutex); + return m_graphicsContext; +} + + +QVector<QString> GLShader::uniformsNames() const +{ + return m_uniformsNames; +} + +QVector<QString> GLShader::attributesNames() const +{ + return m_attributesNames; +} + +QVector<QString> GLShader::uniformBlockNames() const +{ + return m_uniformBlockNames; +} + +QVector<QString> GLShader::storageBlockNames() const +{ + return m_shaderStorageBlockNames; +} + +QVector<QByteArray> GLShader::shaderCode() const +{ + return m_shaderCode; +} + +QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const +{ + return m_uniformBlockIndexToShaderUniforms.value(blockIndex); +} + +ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) +{ + for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { + if (m_uniformBlocks[i].m_index == blockIndex) { + return m_uniformBlocks[i]; + } + } + return ShaderUniformBlock(); +} + +ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) +{ + for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { + if (m_uniformBlocks[i].m_nameId == blockNameId) { + return m_uniformBlocks[i]; + } + } + return ShaderUniformBlock(); +} + +ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) +{ + for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { + if (m_uniformBlocks[i].m_name == blockName) { + return m_uniformBlocks[i]; + } + } + return ShaderUniformBlock(); +} + +ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) +{ + for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { + if (m_shaderStorageBlocks[i].m_index == blockIndex) + return m_shaderStorageBlocks[i]; + } + return ShaderStorageBlock(); +} + +ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) +{ + for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { + if (m_shaderStorageBlocks[i].m_nameId == blockNameId) + return m_shaderStorageBlocks[i]; + } + return ShaderStorageBlock(); +} + +ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) +{ + for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { + if (m_shaderStorageBlocks[i].m_name == blockName) + return m_shaderStorageBlocks[i]; + } + return ShaderStorageBlock(); +} + +void GLShader::prepareUniforms(ShaderParameterPack &pack) +{ + const PackUniformHash &values = pack.uniforms(); + + auto it = values.keys.cbegin(); + const auto end = values.keys.cend(); + + while (it != end) { + // Find if there's a uniform with the same name id + for (const ShaderUniform &uniform : qAsConst(m_uniforms)) { + if (uniform.m_nameId == *it) { + pack.setSubmissionUniform(uniform); + break; + } + } + ++it; + } +} + +void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs) +{ + { + QMutexLocker lock(&m_mutex); + m_fragOutputs = fragOutputs; + } +// updateDNA(); +} + +const QHash<QString, int> GLShader::fragOutputs() const +{ + QMutexLocker lock(&m_mutex); + return m_fragOutputs; +} + +void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescription) +{ + m_uniforms = uniformsDescription; + m_uniformsNames.resize(uniformsDescription.size()); + m_uniformsNamesIds.reserve(uniformsDescription.size()); + m_standardUniformNamesIds.reserve(5); + QHash<QString, ShaderUniform> activeUniformsInDefaultBlock; + + static const QVector<int> standardUniformNameIds = { + Shader::modelMatrixNameId, + Shader::viewMatrixNameId, + Shader::projectionMatrixNameId, + Shader::modelViewMatrixNameId, + Shader::viewProjectionMatrixNameId, + Shader::modelViewProjectionNameId, + Shader::mvpNameId, + Shader::inverseModelMatrixNameId, + Shader::inverseViewMatrixNameId, + Shader::inverseProjectionMatrixNameId, + Shader::inverseModelViewNameId, + Shader::inverseViewProjectionMatrixNameId, + Shader::inverseModelViewProjectionNameId, + Shader::modelNormalMatrixNameId, + Shader::modelViewNormalNameId, + Shader::viewportMatrixNameId, + Shader::inverseViewportMatrixNameId, + Shader::aspectRatioNameId, + Shader::exposureNameId, + Shader::gammaNameId, + Shader::timeNameId, + Shader::eyePositionNameId, + Shader::skinningPaletteNameId, + }; + + for (int i = 0, m = uniformsDescription.size(); i < m; i++) { + m_uniformsNames[i] = m_uniforms[i].m_name; + const int nameId = StringToInt::lookupId(m_uniformsNames[i]); + m_uniforms[i].m_nameId = nameId; + + // Is the uniform a Qt3D "Standard" uniform or a user defined one? + if (standardUniformNameIds.contains(nameId)) + m_standardUniformNamesIds.push_back(nameId); + else + m_uniformsNamesIds.push_back(nameId); + + if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block + qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex; + activeUniformsInDefaultBlock.insert(uniformsDescription[i].m_name, uniformsDescription[i]); + } + } + m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock); +} + +void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription) +{ + m_attributes = attributesDescription; + m_attributesNames.resize(attributesDescription.size()); + m_attributeNamesIds.resize(attributesDescription.size()); + for (int i = 0, m = attributesDescription.size(); i < m; i++) { + m_attributesNames[i] = attributesDescription[i].m_name; + m_attributes[i].m_nameId = StringToInt::lookupId(m_attributesNames[i]); + m_attributeNamesIds[i] = m_attributes[i].m_nameId; + qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name; + } +} + +void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription) +{ + m_uniformBlocks = uniformBlockDescription; + m_uniformBlockNames.resize(uniformBlockDescription.size()); + m_uniformBlockNamesIds.resize(uniformBlockDescription.size()); + for (int i = 0, m = uniformBlockDescription.size(); i < m; ++i) { + m_uniformBlockNames[i] = m_uniformBlocks[i].m_name; + m_uniformBlockNamesIds[i] = StringToInt::lookupId(m_uniformBlockNames[i]); + m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i]; + qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}"; + + // Find all active uniforms for the shader block + QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin(); + const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend(); + + QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin(); + const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend(); + + QHash<QString, ShaderUniform> activeUniformsInBlock; + + while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) { + if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) { + QString uniformName = *uniformNamesIt; + if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(m_uniformBlockNames[i])) + uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt; + activeUniformsInBlock.insert(uniformName, *uniformsIt); + qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex; + } + ++uniformsIt; + ++uniformNamesIt; + } + m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock); + } +} + +void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription) +{ + m_shaderStorageBlocks = shaderStorageBlockDescription; + m_shaderStorageBlockNames.resize(shaderStorageBlockDescription.size()); + m_shaderStorageBlockNamesIds.resize(shaderStorageBlockDescription.size()); + + for (int i = 0, m = shaderStorageBlockDescription.size(); i < m; ++i) { + m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name; + m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(m_shaderStorageBlockNames[i]); + m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i]; + qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}"; + } +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/glshader_p.h b/src/plugins/renderers/opengl/renderer/glshader_p.h new file mode 100644 index 000000000..18293ac54 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/glshader_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GLSHADER_P_H +#define QT3DRENDER_RENDER_GLSHADER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <shadervariables_p.h> +#include <shaderparameterpack_p.h> +#include <Qt3DRender/qshaderprogram.h> +#include <QMutex> + + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; + +namespace Qt3DRender { + +namespace Render { + +class Q_AUTOTEST_EXPORT GLShader +{ +public: + GLShader(); + + void setGraphicsContext(GraphicsContext *context); + GraphicsContext *graphicsContext(); + + bool isLoaded() const { return m_isLoaded; } + void setLoaded(bool loaded) { m_isLoaded = loaded; } + + void prepareUniforms(ShaderParameterPack &pack); + + void setFragOutputs(const QHash<QString, int> &fragOutputs); + const QHash<QString, int> fragOutputs() const; + + inline QVector<int> uniformsNamesIds() const { return m_uniformsNamesIds; } + inline QVector<int> standardUniformNameIds() const { return m_standardUniformNamesIds; } + inline QVector<int> uniformBlockNamesIds() const { return m_uniformBlockNamesIds; } + inline QVector<int> storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; } + inline QVector<int> attributeNamesIds() const { return m_attributeNamesIds; } + + QVector<QString> uniformsNames() const; + QVector<QString> attributesNames() const; + QVector<QString> uniformBlockNames() const; + QVector<QString> storageBlockNames() const; + + inline QVector<ShaderUniform> uniforms() const { return m_uniforms; } + inline QVector<ShaderAttribute> attributes() const { return m_attributes; } + inline QVector<ShaderUniformBlock> uniformBlocks() const { return m_uniformBlocks; } + inline QVector<ShaderStorageBlock> storageBlocks() const { return m_shaderStorageBlocks; } + + QHash<QString, ShaderUniform> activeUniformsForUniformBlock(int blockIndex) const; + + ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId); + ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex); + ShaderUniformBlock uniformBlockForBlockName(const QString &blockName); + + ShaderStorageBlock storageBlockForBlockIndex(int blockIndex); + ShaderStorageBlock storageBlockForBlockNameId(int blockNameId); + ShaderStorageBlock storageBlockForBlockName(const QString &blockName); + + QOpenGLShaderProgram *shaderProgram() { return &m_shader; } + + void setShaderCode(const QVector<QByteArray> shaderCode) { m_shaderCode = shaderCode; } + QVector<QByteArray> shaderCode() const; + +private: + bool m_isLoaded; + QOpenGLShaderProgram m_shader; + GraphicsContext *m_graphicsContext; + + QVector<QString> m_uniformsNames; + QVector<int> m_uniformsNamesIds; + QVector<int> m_standardUniformNamesIds; + QVector<ShaderUniform> m_uniforms; + + QVector<QString> m_attributesNames; + QVector<int> m_attributeNamesIds; + QVector<ShaderAttribute> m_attributes; + + QVector<QString> m_uniformBlockNames; + QVector<int> m_uniformBlockNamesIds; + QVector<ShaderUniformBlock> m_uniformBlocks; + QHash<int, QHash<QString, ShaderUniform> > m_uniformBlockIndexToShaderUniforms; + + QVector<QString> m_shaderStorageBlockNames; + QVector<int> m_shaderStorageBlockNamesIds; + QVector<ShaderStorageBlock> m_shaderStorageBlocks; + + QHash<QString, int> m_fragOutputs; + QVector<QByteArray> m_shaderCode; + + // Private so that only GraphicContext can call it + void initializeUniforms(const QVector<ShaderUniform> &uniformsDescription); + void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription); + void initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription); + void initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription); + + friend class GraphicsContext; + + mutable QMutex m_mutex; + QMetaObject::Connection m_contextConnection; +}; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLSHADER_P_H diff --git a/src/plugins/renderers/opengl/renderer/logging.cpp b/src/plugins/renderers/opengl/renderer/logging.cpp new file mode 100644 index 000000000..ab7437eba --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/logging.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "logging_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +Q_LOGGING_CATEGORY(Backend, "Qt3D.Renderer.OpenGL.Backend", QtWarningMsg) +Q_LOGGING_CATEGORY(Frontend, "Qt3D.Renderer.OpenGL.Frontend", QtWarningMsg) +Q_LOGGING_CATEGORY(Io, "Qt3D.Renderer.OpenGL.IO", QtWarningMsg) +Q_LOGGING_CATEGORY(Jobs, "Qt3D.Renderer.OpenGL.Jobs", QtWarningMsg) +Q_LOGGING_CATEGORY(SceneLoaders, "Qt3D.Renderer.OpenGL.SceneLoaders", QtWarningMsg) +Q_LOGGING_CATEGORY(Framegraph, "Qt3D.Renderer.OpenGL.Framegraph", QtWarningMsg) +Q_LOGGING_CATEGORY(RenderNodes, "Qt3D.Renderer.OpenGL.RenderNodes", QtWarningMsg) +Q_LOGGING_CATEGORY(Rendering, "Qt3D.Renderer.OpenGL.Rendering", QtWarningMsg) +Q_LOGGING_CATEGORY(Memory, "Qt3D.Renderer.OpenGL.Memory", QtWarningMsg) +Q_LOGGING_CATEGORY(Shaders, "Qt3D.Renderer.OpenGL.Shaders", QtWarningMsg) +Q_LOGGING_CATEGORY(RenderStates, "Qt3D.Renderer.OpenGL.RenderStates", QtWarningMsg) +Q_LOGGING_CATEGORY(VSyncAdvanceService, "Qt3D.Renderer.OpenGL.VsyncAdvanceService", QtWarningMsg) + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/logging_p.h b/src/plugins/renderers/opengl/renderer/logging_p.h new file mode 100644 index 000000000..8a550e22f --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/logging_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_OPENGL_RENDERLOGGING_P_H +#define QT3DRENDER_RENDER_OPENGL_RENDERLOGGING_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +Q_DECLARE_LOGGING_CATEGORY(Backend) +Q_DECLARE_LOGGING_CATEGORY(Frontend) +Q_DECLARE_LOGGING_CATEGORY(Io) +Q_DECLARE_LOGGING_CATEGORY(Jobs) +Q_DECLARE_LOGGING_CATEGORY(SceneLoaders) +Q_DECLARE_LOGGING_CATEGORY(Framegraph) +Q_DECLARE_LOGGING_CATEGORY(RenderNodes) +Q_DECLARE_LOGGING_CATEGORY(Rendering) +Q_DECLARE_LOGGING_CATEGORY(Memory) +Q_DECLARE_LOGGING_CATEGORY(Shaders) +Q_DECLARE_LOGGING_CATEGORY(RenderStates) +Q_DECLARE_LOGGING_CATEGORY(VSyncAdvanceService) + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_OPENGL_RENDERLOGGING_P_H diff --git a/src/plugins/renderers/opengl/renderer/openglvertexarrayobject.cpp b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject.cpp new file mode 100644 index 000000000..19ae4fa55 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "openglvertexarrayobject_p.h" +#include <submissioncontext_p.h> +#include <renderer_p.h> +#include <glresourcemanagers_p.h> +#include <Qt3DRender/private/managers_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +OpenGLVertexArrayObject::OpenGLVertexArrayObject() + : m_ctx(nullptr) + , m_specified(false) + , m_supportsVao(false) +{} + +void OpenGLVertexArrayObject::bind() +{ + Q_ASSERT(m_ctx); + if (m_supportsVao) { + Q_ASSERT(!m_vao.isNull()); + Q_ASSERT(m_vao->isCreated()); + m_vao->bind(); + } else { + // Unbind any other VAO that may have been bound and not released correctly + if (m_ctx->m_currentVAO != nullptr && m_ctx->m_currentVAO != this) + m_ctx->m_currentVAO->release(); + + m_ctx->m_currentVAO = this; + // We need to specify array and vertex attributes + for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) + m_ctx->enableAttribute(attr); + if (!m_indexAttribute.isNull()) + m_ctx->bindGLBuffer(m_ctx->m_renderer->glResourceManagers()->glBufferManager()->data(m_indexAttribute), + GLBuffer::IndexBuffer); + } +} + +void OpenGLVertexArrayObject::release() +{ + Q_ASSERT(m_ctx); + if (m_supportsVao) { + Q_ASSERT(!m_vao.isNull()); + Q_ASSERT(m_vao->isCreated()); + m_vao->release(); + } else { + if (m_ctx->m_currentVAO == this) { + for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) + m_ctx->disableAttribute(attr); + m_ctx->m_currentVAO = nullptr; + } + } +} + +// called from Render thread +void OpenGLVertexArrayObject::create(SubmissionContext *ctx, const VAOIdentifier &key) +{ + QMutexLocker lock(&m_mutex); + + Q_ASSERT(!m_ctx && !m_vao); + + m_ctx = ctx; + m_supportsVao = m_ctx->supportsVAO(); + if (m_supportsVao) { + m_vao.reset(new QOpenGLVertexArrayObject()); + m_vao->create(); + } + m_owners = key; +} + +VAOIdentifier OpenGLVertexArrayObject::key() const +{ + return m_owners; +} + +// called from Render thread +void OpenGLVertexArrayObject::destroy() +{ + QMutexLocker locker(&m_mutex); + + Q_ASSERT(m_ctx); + cleanup(); +} + +void OpenGLVertexArrayObject::cleanup() +{ + m_vao.reset(); + m_ctx = nullptr; + m_specified = false; + m_supportsVao = false; + m_indexAttribute = SubmissionContext::VAOIndexAttribute(); + m_vertexAttributes.clear(); +} + +// called from job +bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, GLShaderManager *shaderMgr) +{ + QMutexLocker lock(&m_mutex); + + if (!m_ctx) + return false; + + const bool geometryExists = (geomMgr->data(m_owners.first) != nullptr); + const bool shaderExists = (shaderMgr->lookupResource(m_owners.second) != nullptr); + + return !geometryExists || !shaderExists; +} + +void OpenGLVertexArrayObject::saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr) +{ + // Remove any vertexAttribute already at location + for (auto i = m_vertexAttributes.size() - 1; i >= 0; --i) { + if (m_vertexAttributes.at(i).location == attr.location) { + m_vertexAttributes.removeAt(i); + break; + } + } + m_vertexAttributes.push_back(attr); +} + + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/openglvertexarrayobject_p.h b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject_p.h new file mode 100644 index 000000000..d422b5b8b --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPENGLVERTEXARRAYOBJECT_H +#define OPENGLVERTEXARRAYOBJECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qopenglvertexarrayobject.h> +#include <submissioncontext_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class GeometryManager; +class GLShaderManager; + +typedef QPair<HGeometry, Qt3DCore::QNodeId> VAOIdentifier; + +class OpenGLVertexArrayObject +{ +public: + OpenGLVertexArrayObject(); + + void bind(); + void release(); + + void create(SubmissionContext *ctx, const VAOIdentifier &key); + VAOIdentifier key() const; + void destroy(); + void cleanup(); + + bool isAbandoned(GeometryManager *geomMgr, GLShaderManager *shaderMgr); + + QOpenGLVertexArrayObject *vao() { return m_vao.data(); } + const QOpenGLVertexArrayObject *vao() const { return m_vao.data(); } + + void setSpecified(bool b) { m_specified = b; } + bool isSpecified() const { return m_specified; } + + +private: + QMutex m_mutex; + SubmissionContext *m_ctx; + QScopedPointer<QOpenGLVertexArrayObject> m_vao; + bool m_specified; + bool m_supportsVao; + VAOIdentifier m_owners; + + friend class SubmissionContext; + + void saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr); + inline void saveIndexAttribute(HGLBuffer glBufferHandle) { m_indexAttribute = glBufferHandle; } + + QVector<SubmissionContext::VAOVertexAttribute> m_vertexAttributes; + SubmissionContext::VAOIndexAttribute m_indexAttribute; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // OPENGLVERTEXARRAYOBJECT_H diff --git a/src/plugins/renderers/opengl/renderer/rendercommand.cpp b/src/plugins/renderers/opengl/renderer/rendercommand.cpp new file mode 100644 index 000000000..c6d42fde1 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/rendercommand.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rendercommand_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +RenderCommand::RenderCommand() + : m_glShader(nullptr) + , m_stateSet(nullptr) + , m_depth(0.0f) + , m_changeCost(0) + , m_type(RenderCommand::Draw) + , m_primitiveCount(0) + , m_primitiveType(QGeometryRenderer::Triangles) + , m_restartIndexValue(-1) + , m_firstInstance(0) + , m_firstVertex(0) + , m_verticesPerPatch(0) + , m_instanceCount(0) + , m_indexOffset(0) + , m_indexAttributeByteOffset(0) + , m_indexAttributeDataType(GL_UNSIGNED_SHORT) + , m_indirectAttributeByteOffset(0) + , m_drawIndexed(false) + , m_drawIndirect(false) + , m_primitiveRestartEnabled(false) + , m_isValid(false) +{ + m_workGroups[0] = 0; + m_workGroups[1] = 0; + m_workGroups[2] = 0; +} + +bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept +{ + return (a.m_vao == b.m_vao && a.m_glShader == b.m_glShader && a.m_material == b.m_material && + a.m_stateSet == b.m_stateSet && a.m_geometry == b.m_geometry && a.m_geometryRenderer == b.m_geometryRenderer && + a.m_indirectDrawBuffer == b.m_indirectDrawBuffer && a.m_activeAttributes == b.m_activeAttributes && + a.m_depth == b.m_depth && a.m_changeCost == b.m_changeCost && a.m_shaderId == b.m_shaderId && + a.m_workGroups[0] == b.m_workGroups[0] && a.m_workGroups[1] == b.m_workGroups[1] && a.m_workGroups[2] == b.m_workGroups[2] && + a.m_primitiveCount == b.m_primitiveCount && a.m_primitiveType == b.m_primitiveType && a.m_restartIndexValue == b.m_restartIndexValue && + a.m_firstInstance == b.m_firstInstance && a.m_firstVertex == b.m_firstVertex && a.m_verticesPerPatch == b.m_verticesPerPatch && + a.m_instanceCount == b.m_instanceCount && a.m_indexOffset == b.m_indexOffset && a.m_indexAttributeByteOffset == b.m_indexAttributeByteOffset && + a.m_drawIndexed == b.m_drawIndexed && a.m_drawIndirect == b.m_drawIndirect && a.m_primitiveRestartEnabled == b.m_primitiveRestartEnabled && + a.m_isValid == b.m_isValid && a.m_computeCommand == b.m_computeCommand); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/rendercommand_p.h b/src/plugins/renderers/opengl/renderer/rendercommand_p.h new file mode 100644 index 000000000..600c52959 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/rendercommand_p.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERCOMMAND_H +#define QT3DRENDER_RENDER_RENDERCOMMAND_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qglobal.h> +#include <shaderparameterpack_p.h> +#include <gl_handle_types_p.h> +#include <renderviewjobutils_p.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/qgeometryrenderer.h> +#include <QOpenGLShaderProgram> +#include <QOpenGLTexture> +#include <QMatrix4x4> + +QT_BEGIN_NAMESPACE + +class QOpenGLVertexArrayObject; + +namespace Qt3DRender { + +namespace Render { + +class RenderStateSet; +using RenderStateSetPtr = QSharedPointer<RenderStateSet>; +class GLShader; + +class Q_AUTOTEST_EXPORT RenderCommand +{ +public: + RenderCommand(); + + HVao m_vao; // VAO used during the submission step to store all states and VBOs + HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....) + GLShader *m_glShader; // GL Shader to be used at render time + Qt3DCore::QNodeId m_shaderId; // Shader for given pass and mesh + ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy the + // Texture while submission is happening. + RenderStateSetPtr m_stateSet; + + HGeometry m_geometry; + HGeometryRenderer m_geometryRenderer; + + HBuffer m_indirectDrawBuffer; // Reference to indirect draw buffer (valid only m_drawIndirect == true) + HComputeCommand m_computeCommand; + + // A QAttribute pack might be interesting + // This is a temporary fix in the meantime, to remove the hacked methods in Technique + QVector<int> m_activeAttributes; + + float m_depth; + int m_changeCost; + + enum CommandType { + Draw, + Compute + }; + + CommandType m_type; + int m_workGroups[3]; + + // Values filled for draw calls by Renderer (in prepare Submission) + GLsizei m_primitiveCount; + QGeometryRenderer::PrimitiveType m_primitiveType; + int m_restartIndexValue; + int m_firstInstance; + int m_firstVertex; + int m_verticesPerPatch; + int m_instanceCount; + int m_indexOffset; + uint m_indexAttributeByteOffset; + GLint m_indexAttributeDataType; + uint m_indirectAttributeByteOffset; + bool m_drawIndexed; + bool m_drawIndirect; + bool m_primitiveRestartEnabled; + bool m_isValid; +}; + +Q_AUTOTEST_EXPORT bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept; + +inline bool operator!=(const RenderCommand &lhs, const RenderCommand &rhs) noexcept +{ return !operator==(lhs, rhs); } + +struct EntityRenderCommandData +{ + QVector<Entity *> entities; + QVector<RenderCommand> commands; + QVector<RenderPassParameterData> passesData; + + void reserve(int size) + { + entities.reserve(size); + commands.reserve(size); + passesData.reserve(size); + } + + inline int size() const { return entities.size(); } + + inline void push_back(Entity *e, const RenderCommand &c, const RenderPassParameterData &p) + { + entities.push_back(e); + commands.push_back(c); + passesData.push_back(p); + } + + inline void push_back(Entity *e, RenderCommand &&c, RenderPassParameterData &&p) + { + entities.push_back(e); + commands.push_back(std::move(c)); + passesData.push_back(std::move(p)); + } + + EntityRenderCommandData &operator+=(EntityRenderCommandData &&t) + { + entities += std::move(t.entities); + commands += std::move(t.commands); + passesData += std::move(t.passesData); + return *this; + } + +}; + +using EntityRenderCommandDataPtr = QSharedPointer<EntityRenderCommandData>; + + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERCOMMAND_H diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp new file mode 100644 index 000000000..a090f7abb --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderer.cpp @@ -0,0 +1,2414 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderer_p.h" + +#include <Qt3DCore/qentity.h> + +#include <Qt3DRender/qmaterial.h> +#include <Qt3DRender/qmesh.h> +#include <Qt3DRender/qrenderpass.h> +#include <Qt3DRender/qshaderprogram.h> +#include <Qt3DRender/qtechnique.h> +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DRender/qeffect.h> + +#include <Qt3DRender/private/qsceneimporter_p.h> +#include <Qt3DRender/private/renderstates_p.h> +#include <Qt3DRender/private/cameraselectornode_p.h> +#include <Qt3DRender/private/framegraphvisitor_p.h> +#include <Qt3DRender/private/cameralens_p.h> +#include <Qt3DRender/private/entity_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/material_p.h> +#include <Qt3DRender/private/renderpassfilternode_p.h> +#include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/technique_p.h> +#include <Qt3DRender/private/renderthread_p.h> +#include <Qt3DRender/private/scenemanager_p.h> +#include <Qt3DRender/private/techniquefilternode_p.h> +#include <Qt3DRender/private/viewportnode_p.h> +#include <Qt3DRender/private/vsyncframeadvanceservice_p.h> +#include <Qt3DRender/private/pickeventfilter_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/geometryrenderermanager_p.h> +#include <Qt3DRender/private/techniquemanager_p.h> +#include <Qt3DRender/private/platformsurfacefilter_p.h> +#include <Qt3DRender/private/loadbufferjob_p.h> +#include <Qt3DRender/private/rendercapture_p.h> +#include <Qt3DRender/private/updatelevelofdetailjob_p.h> +#include <Qt3DRender/private/buffercapture_p.h> +#include <Qt3DRender/private/offscreensurfacehelper_p.h> +#include <Qt3DRender/private/subtreeenabler_p.h> +#include <Qt3DRender/private/qshaderprogrambuilder_p.h> +#include <Qt3DRender/private/qshaderprogram_p.h> +#include <Qt3DRender/private/filterentitybycomponentjob_p.h> + +#include <Qt3DRender/qcameralens.h> +#include <Qt3DCore/private/qeventfilterservice_p.h> +#include <Qt3DCore/private/qabstractaspectjobmanager_p.h> +#include <Qt3DCore/private/qaspectmanager_p.h> +#include <Qt3DCore/private/qsysteminformationservice_p.h> +#include <Qt3DCore/private/qsysteminformationservice_p_p.h> +#include <Qt3DRender/private/resourceaccessor_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/setfence_p.h> + +#include <glbuffer_p.h> +#include <graphicscontext_p.h> +#include <rendercommand_p.h> +#include <renderqueue_p.h> +#include <renderview_p.h> +#include <gltexture_p.h> +#include <openglvertexarrayobject_p.h> +#include <renderviewbuilder_p.h> +#include <glresourcemanagers_p.h> +#include <commandexecuter_p.h> + +#include <QStack> +#include <QOffscreenSurface> +#include <QSurface> +#include <QElapsedTimer> +#include <QLibraryInfo> +#include <QMutexLocker> +#include <QPluginLoader> +#include <QDir> +#include <QUrl> +#include <QOffscreenSurface> +#include <QWindow> +#include <QThread> + +#include <QtGui/private/qopenglcontext_p.h> +#include <Qt3DRender/private/frameprofiler_p.h> + +QT_BEGIN_NAMESPACE + +// Crashes on AMD Radeon drivers on Windows. Disable for now. +//#define SHADER_LOADING_IN_COMMAND_THREAD + +using namespace Qt3DCore; + +namespace Qt3DRender { +namespace Render { + +namespace { + +class SyncLightsGatherer +{ +public: + explicit SyncLightsGatherer(LightGathererPtr gatherJob, + RendererCache *cache) + : m_gatherJob(gatherJob) + , m_cache(cache) + { + } + + void operator()() + { + QMutexLocker lock(m_cache->mutex()); + m_cache->gatheredLights = m_gatherJob->lights(); + m_cache->environmentLight = m_gatherJob->takeEnvironmentLight(); + } + +private: + LightGathererPtr m_gatherJob; + RendererCache *m_cache; +}; + +class SyncRenderableEntities +{ +public: + explicit SyncRenderableEntities(RenderableEntityFilterPtr gatherJob, + RendererCache *cache) + : m_gatherJob(gatherJob) + , m_cache(cache) + { + } + + void operator()() + { + QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities(); + std::sort(selectedEntities.begin(), selectedEntities.end()); + + QMutexLocker lock(m_cache->mutex()); + m_cache->renderableEntities = selectedEntities; + } + +private: + RenderableEntityFilterPtr m_gatherJob; + RendererCache *m_cache; +}; + +class SyncComputableEntities +{ +public: + explicit SyncComputableEntities(ComputableEntityFilterPtr gatherJob, + RendererCache *cache) + : m_gatherJob(gatherJob) + , m_cache(cache) + { + } + + void operator()() + { + QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities(); + std::sort(selectedEntities.begin(), selectedEntities.end()); + + QMutexLocker lock(m_cache->mutex()); + m_cache->computeEntities = selectedEntities; + } + +private: + ComputableEntityFilterPtr m_gatherJob; + RendererCache *m_cache; +}; + +} // anonymous + + +/*! + \internal + + Renderer shutdown procedure: + + Since the renderer relies on the surface and OpenGLContext to perform its cleanup, + it is shutdown when the surface is set to nullptr + + When the surface is set to nullptr this will request the RenderThread to terminate + and will prevent createRenderBinJobs from returning a set of jobs as there is nothing + more to be rendered. + + In turn, this will call shutdown which will make the OpenGL context current one last time + to allow cleanups requiring a call to QOpenGLContext::currentContext to execute properly. + At the end of that function, the GraphicsContext is set to null. + + At this point though, the QAspectThread is still running its event loop and will only stop + a short while after. + */ + +Renderer::Renderer(QRenderAspect::RenderType type) + : m_services(nullptr) + , m_nodesManager(nullptr) + , m_renderSceneRoot(nullptr) + , m_defaultRenderStateSet(nullptr) + , m_submissionContext(nullptr) + , m_renderQueue(new RenderQueue()) + , m_renderThread(type == QRenderAspect::Threaded ? new RenderThread(this) : nullptr) + , m_vsyncFrameAdvanceService(new VSyncFrameAdvanceService(m_renderThread != nullptr)) + , m_waitForInitializationToBeCompleted(0) + , m_hasBeenInitializedMutex() + , m_pickEventFilter(new PickEventFilter()) + , m_exposed(0) + , m_lastFrameCorrect(0) + , m_glContext(nullptr) + , m_shareContext(nullptr) + , m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create()) + , m_rayCastingJob(RayCastingJobPtr::create()) + , m_time(0) + , m_settings(nullptr) + , m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create()) + , m_cleanupJob(Render::FrameCleanupJobPtr::create()) + , m_worldTransformJob(Render::UpdateWorldTransformJobPtr::create()) + , m_expandBoundingVolumeJob(Render::ExpandBoundingVolumeJobPtr::create()) + , m_calculateBoundingVolumeJob(Render::CalculateBoundingVolumeJobPtr::create()) + , m_updateWorldBoundingVolumeJob(Render::UpdateWorldBoundingVolumeJobPtr::create()) + , m_updateTreeEnabledJob(Render::UpdateTreeEnabledJobPtr::create()) + , m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create()) + , m_updateSkinningPaletteJob(Render::UpdateSkinningPaletteJobPtr::create()) + , m_updateLevelOfDetailJob(Render::UpdateLevelOfDetailJobPtr::create()) + , m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create()) + , m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create()) + , m_updateEntityLayersJob(Render::UpdateEntityLayersJobPtr::create()) + , m_lightGathererJob(Render::LightGathererPtr::create()) + , m_renderableEntityFilterJob(Render::RenderableEntityFilterPtr::create()) + , m_computableEntityFilterJob(Render::ComputableEntityFilterPtr::create()) + , m_bufferGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering)) + , m_vaoGathererJob(SynchronizerJobPtr::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering)) + , m_textureGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering)) + , m_sendSetFenceHandlesToFrontendJob(SynchronizerJobPtr::create([this] { sendSetFenceHandlesToFrontend(); }, JobTypes::SendSetFenceHandlesToFrontend)) + , m_introspectShaderJob(SynchronizerPostFramePtr::create([this] { reloadDirtyShaders(); }, + [this] (Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); }, + JobTypes::DirtyShaderGathering)) + , m_syncLoadingJobs(SynchronizerJobPtr::create([] {}, JobTypes::SyncLoadingJobs)) + , m_cacheRenderableEntitiesJob(SynchronizerJobPtr::create(SyncRenderableEntities(m_renderableEntityFilterJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) + , m_cacheComputableEntitiesJob(SynchronizerJobPtr::create(SyncComputableEntities(m_computableEntityFilterJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) + , m_cacheLightsJob(SynchronizerJobPtr::create(SyncLightsGatherer(m_lightGathererJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) + , m_ownedContext(false) + , m_offscreenHelper(nullptr) + , m_glResourceManagers(nullptr) + , m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)) + , m_shouldSwapBuffers(true) +{ + // Set renderer as running - it will wait in the context of the + // RenderThread for RenderViews to be submitted + m_running.fetchAndStoreOrdered(1); + if (m_renderThread) + m_renderThread->waitForStart(); + + // Create jobs to update transforms and bounding volumes + // We can only update bounding volumes once all world transforms are known + m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob); + m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob); + m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob); + m_updateShaderDataTransformJob->addDependency(m_worldTransformJob); + m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob); + m_rayCastingJob->addDependency(m_expandBoundingVolumeJob); + // m_calculateBoundingVolumeJob's dependency on m_updateTreeEnabledJob is set in renderBinJobs + + // Ensures all skeletons are loaded before we try to update them + m_updateSkinningPaletteJob->addDependency(m_syncLoadingJobs); + + // All world stuff depends on the RenderEntity's localBoundingVolume + m_updateLevelOfDetailJob->addDependency(m_updateMeshTriangleListJob); + m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob); + m_rayCastingJob->addDependency(m_updateMeshTriangleListJob); + + m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob); + + m_cacheLightsJob->addDependency(m_lightGathererJob); + m_cacheRenderableEntitiesJob->addDependency(m_renderableEntityFilterJob); + m_cacheComputableEntitiesJob->addDependency(m_computableEntityFilterJob); + + m_filterCompatibleTechniqueJob->setRenderer(this); + + m_defaultRenderStateSet = new RenderStateSet; + m_defaultRenderStateSet->addState(StateVariant::createState<DepthTest>(GL_LESS)); + m_defaultRenderStateSet->addState(StateVariant::createState<CullFace>(GL_BACK)); + m_defaultRenderStateSet->addState(StateVariant::createState<ColorMask>(true, true, true, true)); +} + +Renderer::~Renderer() +{ + Q_ASSERT(m_running.fetchAndStoreOrdered(0) == 0); + if (m_renderThread) + Q_ASSERT(m_renderThread->isFinished()); + + delete m_renderQueue; + delete m_defaultRenderStateSet; + delete m_glResourceManagers; + + if (!m_ownedContext) + QObject::disconnect(m_contextConnection); +} + +void Renderer::dumpInfo() const +{ + qDebug() << Q_FUNC_INFO << "t =" << m_time; + + const ShaderManager *shaderManager = m_nodesManager->shaderManager(); + qDebug() << "=== Shader Manager ==="; + qDebug() << *shaderManager; + + const TextureManager *textureManager = m_nodesManager->textureManager(); + qDebug() << "=== Texture Manager ==="; + qDebug() << *textureManager; + + const TextureImageManager *textureImageManager = m_nodesManager->textureImageManager(); + qDebug() << "=== Texture Image Manager ==="; + qDebug() << *textureImageManager; +} + +qint64 Renderer::time() const +{ + return m_time; +} + +void Renderer::setTime(qint64 time) +{ + m_time = time; +} + +void Renderer::setNodeManagers(NodeManagers *managers) +{ + m_nodesManager = managers; + m_glResourceManagers = new GLResourceManagers(); + m_scene2DResourceAccessor.reset(new ResourceAccessor(this, m_nodesManager)); + + m_updateShaderDataTransformJob->setManagers(m_nodesManager); + m_cleanupJob->setManagers(m_nodesManager); + m_calculateBoundingVolumeJob->setManagers(m_nodesManager); + m_expandBoundingVolumeJob->setManagers(m_nodesManager); + m_worldTransformJob->setManagers(m_nodesManager); + m_pickBoundingVolumeJob->setManagers(m_nodesManager); + m_rayCastingJob->setManagers(m_nodesManager); + m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager()); + m_updateLevelOfDetailJob->setManagers(m_nodesManager); + m_updateSkinningPaletteJob->setManagers(m_nodesManager); + m_updateMeshTriangleListJob->setManagers(m_nodesManager); + m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager()); + m_updateEntityLayersJob->setManager(m_nodesManager); + m_updateTreeEnabledJob->setManagers(m_nodesManager); + m_sendBufferCaptureJob->setManagers(m_nodesManager); + m_lightGathererJob->setManager(m_nodesManager->renderNodesManager()); + m_renderableEntityFilterJob->setManager(m_nodesManager->renderNodesManager()); + m_computableEntityFilterJob->setManager(m_nodesManager->renderNodesManager()); +} + +void Renderer::setServices(QServiceLocator *services) +{ + m_services = services; + + m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService()); +} + +NodeManagers *Renderer::nodeManagers() const +{ + return m_nodesManager; +} + +/*! + \internal + + Return context which can be used to share resources safely + with qt3d main render context. +*/ +QOpenGLContext *Renderer::shareContext() const +{ + QMutexLocker lock(&m_shareContextMutex); + return m_shareContext ? m_shareContext + : (m_submissionContext->openGLContext() + ? m_submissionContext->openGLContext()->shareContext() + : nullptr); +} + +// Executed in the reloadDirtyShader job +void Renderer::loadShader(Shader *shader, HShader shaderHandle) +{ + Q_UNUSED(shader); + if (!m_dirtyShaders.contains(shaderHandle)) + m_dirtyShaders.push_back(shaderHandle); +} + +void Renderer::setOpenGLContext(QOpenGLContext *context) +{ + m_glContext = context; +} + +void Renderer::setScreen(QScreen *scr) +{ + m_screen = scr; +} + +QScreen *Renderer::screen() const +{ + return m_screen; +} + +bool Renderer::accessOpenGLTexture(Qt3DCore::QNodeId nodeId, + QOpenGLTexture **texture, + QMutex **lock, + bool readonly) +{ + Texture *tex = m_nodesManager->textureManager()->lookupResource(nodeId); + if (!tex) + return false; + + GLTexture *glTex = m_glResourceManagers->glTextureManager()->lookupResource(tex->peerId()); + if (!glTex) + return false; + + if (glTex->isDirty()) + return false; + + if (!readonly) + glTex->setExternalRenderingEnabled(true); + + GLTexture::TextureUpdateInfo texInfo = glTex->createOrUpdateGLTexture(); + *texture = texInfo.texture; + + if (!readonly) + *lock = glTex->externalRenderingLock(); + + return true; +} + +QSharedPointer<RenderBackendResourceAccessor> Renderer::resourceAccessor() const +{ + return m_scene2DResourceAccessor; +} + +// Called in RenderThread context by the run method of RenderThread +// RenderThread has locked the mutex already and unlocks it when this +// method termintates +void Renderer::initialize() +{ + QMutexLocker lock(&m_hasBeenInitializedMutex); + m_submissionContext.reset(new SubmissionContext); + m_submissionContext->setRenderer(this); + + QOpenGLContext* ctx = m_glContext; + + { + QMutexLocker lock(&m_shareContextMutex); + // If we are using our own context (not provided by QtQuick), + // we need to create it + if (!m_glContext) { + ctx = new QOpenGLContext; + if (m_screen) + ctx->setScreen(m_screen); + ctx->setShareContext(qt_gl_global_share_context()); + + // TO DO: Shouldn't we use the highest context available and trust + // QOpenGLContext to fall back on the best lowest supported ? + const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING"); + + if (!debugLoggingMode.isEmpty()) { + QSurfaceFormat sf = ctx->format(); + sf.setOption(QSurfaceFormat::DebugContext); + ctx->setFormat(sf); + } + + // Create OpenGL context + if (ctx->create()) + qCDebug(Backend) << "OpenGL context created with actual format" << ctx->format(); + else + qCWarning(Backend) << Q_FUNC_INFO << "OpenGL context creation failed"; + m_ownedContext = true; + } else { + // Context is not owned by us, so we need to know if it gets destroyed + m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed, + [this] { releaseGraphicsResources(); }); + } + + qCDebug(Backend) << "Qt3D shared context:" << ctx->shareContext(); + qCDebug(Backend) << "Qt global shared context:" << qt_gl_global_share_context(); + + if (!ctx->shareContext()) { + m_shareContext = new QOpenGLContext; + if (ctx->screen()) + m_shareContext->setScreen(ctx->screen()); + m_shareContext->setFormat(ctx->format()); + m_shareContext->setShareContext(ctx); + m_shareContext->create(); + } + + // Note: we don't have a surface at this point + // The context will be made current later on (at render time) + m_submissionContext->setOpenGLContext(ctx); + + // Store the format used by the context and queue up creating an + // offscreen surface in the main thread so that it is available + // for use when we want to shutdown the renderer. We need to create + // the offscreen surface on the main thread because on some platforms + // (MS Windows), an offscreen surface is just a hidden QWindow. + m_format = ctx->format(); + QMetaObject::invokeMethod(m_offscreenHelper, "createOffscreenSurface"); + } + + // Awake setScenegraphRoot in case it was waiting + m_waitForInitializationToBeCompleted.release(1); + // Allow the aspect manager to proceed + m_vsyncFrameAdvanceService->proceedToNextFrame(); + + // Force initial refresh + markDirty(AllDirty, nullptr); +} + +/*! + * \internal + * + * Signals for the renderer to stop rendering. If a threaded renderer is in use, + * the render thread will call releaseGraphicsResources() just before the thread exits. + * If rendering synchronously, this function will call releaseGraphicsResources(). + */ +void Renderer::shutdown() +{ + // Ensure we have waited to be fully initialized before trying to shut down + // (in case initialization is taking place at the same time) + QMutexLocker lock(&m_hasBeenInitializedMutex); + + qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer shutdown"; + m_running.storeRelaxed(0); + + // We delete any renderqueue that we may not have had time to render + // before the surface was destroyed + QMutexLocker lockRenderQueue(m_renderQueue->mutex()); + qDeleteAll(m_renderQueue->nextFrameQueue()); + m_renderQueue->reset(); + lockRenderQueue.unlock(); + + if (!m_renderThread) { + releaseGraphicsResources(); + } else { + // Wake up the render thread in case it is waiting for some renderviews + // to be ready. The isReadyToSubmit() function checks for a shutdown + // having been requested. + m_submitRenderViewsSemaphore.release(1); + m_renderThread->wait(); + } + + // Destroy internal managers + // This needs to be done before the nodeManager is destroy + // as the internal resources might somehow rely on nodeManager resources + delete m_glResourceManagers; + m_glResourceManagers = nullptr; +} + +/*! + \internal + + When using a threaded renderer this function is called in the context of the + RenderThread to do any shutdown and cleanup that needs to be performed in the + thread where the OpenGL context lives. + + When using Scene3D or anything that provides a custom QOpenGLContext (not + owned by Qt3D) this function is called whenever the signal + QOpenGLContext::aboutToBeDestroyed is emitted. In that case this function + is called in the context of the emitter's thread. +*/ +void Renderer::releaseGraphicsResources() +{ + // We may get called twice when running inside of a Scene3D. Once when Qt Quick + // wants to shutdown, and again when the render aspect gets unregistered. So + // check that we haven't already cleaned up before going any further. + if (!m_submissionContext) + return; + + // Try to temporarily make the context current so we can free up any resources + QMutexLocker locker(&m_offscreenSurfaceMutex); + QOffscreenSurface *offscreenSurface = m_offscreenHelper->offscreenSurface(); + if (!offscreenSurface) { + qWarning() << "Failed to make context current: OpenGL resources will not be destroyed"; + // We still need to delete the submission context + m_submissionContext.reset(nullptr); + return; + } + + QOpenGLContext *context = m_submissionContext->openGLContext(); + Q_ASSERT(context); + + if (context->thread() == QThread::currentThread() && context->makeCurrent(offscreenSurface)) { + + // Clean up the graphics context and any resources + const QVector<HGLTexture> activeTexturesHandles = m_glResourceManagers->glTextureManager()->activeHandles(); + for (const HGLTexture &textureHandle : activeTexturesHandles) { + GLTexture *tex = m_glResourceManagers->glTextureManager()->data(textureHandle); + tex->destroy(); + } + + // Do the same thing with buffers + const QVector<HGLBuffer> activeBuffers = m_glResourceManagers->glBufferManager()->activeHandles(); + for (const HGLBuffer &bufferHandle : activeBuffers) { + GLBuffer *buffer = m_glResourceManagers->glBufferManager()->data(bufferHandle); + buffer->destroy(m_submissionContext.data()); + } + + // Do the same thing with shaders + const QVector<GLShader *> shaders = m_glResourceManagers->glShaderManager()->takeActiveResources(); + qDeleteAll(shaders); + + // Do the same thing with VAOs + const QVector<HVao> activeVaos = m_glResourceManagers->vaoManager()->activeHandles(); + for (const HVao &vaoHandle : activeVaos) { + OpenGLVertexArrayObject *vao = m_glResourceManagers->vaoManager()->data(vaoHandle); + vao->destroy(); + } + + context->doneCurrent(); + } else { + qWarning() << "Failed to make context current: OpenGL resources will not be destroyed"; + } + + if (m_ownedContext) + delete context; + if (m_shareContext) + delete m_shareContext; + + m_submissionContext.reset(nullptr); + qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown"; +} + +void Renderer::setSurfaceExposed(bool exposed) +{ + qCDebug(Backend) << "Window exposed: " << exposed; + m_exposed.fetchAndStoreOrdered(exposed); +} + +Render::FrameGraphNode *Renderer::frameGraphRoot() const +{ + Q_ASSERT(m_settings); + if (m_nodesManager && m_nodesManager->frameGraphManager() && m_settings) + return m_nodesManager->frameGraphManager()->lookupNode(m_settings->activeFrameGraphID()); + return nullptr; +} + +// QAspectThread context +// Order of execution : +// 1) RenderThread is created -> release 1 of m_waitForInitializationToBeCompleted when started +// 2) setSceneRoot waits to acquire initialization +// 3) submitRenderView -> check for surface +// -> make surface current + create proper glHelper if needed +void Renderer::setSceneRoot(Entity *sgRoot) +{ + Q_ASSERT(sgRoot); + + // If initialization hasn't been completed we must wait + m_waitForInitializationToBeCompleted.acquire(); + + m_renderSceneRoot = sgRoot; + if (!m_renderSceneRoot) + qCWarning(Backend) << "Failed to build render scene"; + m_renderSceneRoot->dump(); + qCDebug(Backend) << Q_FUNC_INFO << "DUMPING SCENE"; + + // Set the scene root on the jobs + m_worldTransformJob->setRoot(m_renderSceneRoot); + m_expandBoundingVolumeJob->setRoot(m_renderSceneRoot); + m_calculateBoundingVolumeJob->setRoot(m_renderSceneRoot); + m_cleanupJob->setRoot(m_renderSceneRoot); + m_pickBoundingVolumeJob->setRoot(m_renderSceneRoot); + m_rayCastingJob->setRoot(m_renderSceneRoot); + m_updateLevelOfDetailJob->setRoot(m_renderSceneRoot); + m_updateSkinningPaletteJob->setRoot(m_renderSceneRoot); + m_updateTreeEnabledJob->setRoot(m_renderSceneRoot); + + // Set all flags to dirty + m_dirtyBits.marked |= AbstractRenderer::AllDirty; +} + +void Renderer::registerEventFilter(QEventFilterService *service) +{ + qCDebug(Backend) << Q_FUNC_INFO << QThread::currentThread(); + service->registerEventFilter(m_pickEventFilter.data(), 1024); +} + +void Renderer::setSettings(RenderSettings *settings) +{ + m_settings = settings; +} + +RenderSettings *Renderer::settings() const +{ + return m_settings; +} + +void Renderer::render() +{ + // Traversing the framegraph tree from root to lead node + // Allows us to define the rendering set up + // Camera, RenderTarget ... + + // Utimately the renderer should be a framework + // For the processing of the list of renderviews + + // Matrice update, bounding volumes computation ... + // Should be jobs + + // namespace Qt3DCore has 2 distincts node trees + // One scene description + // One framegraph description + + while (m_running.loadRelaxed() > 0) { + doRender(); + // TO DO: Restore windows exposed detection + // Probably needs to happens some place else though + } +} + +// Either called by render if Qt3D is in charge of the RenderThread +// or by QRenderAspectPrivate::renderSynchronous (for Scene3D) +void Renderer::doRender(bool swapBuffers) +{ + Renderer::ViewSubmissionResultData submissionData; + bool hasCleanedQueueAndProceeded = false; + bool preprocessingComplete = false; + bool beganDrawing = false; + + // Blocking until RenderQueue is full + const bool canSubmit = isReadyToSubmit(); + m_shouldSwapBuffers = swapBuffers; + + // Lock the mutex to protect access to the renderQueue while we look for its state + QMutexLocker locker(m_renderQueue->mutex()); + const bool queueIsComplete = m_renderQueue->isFrameQueueComplete(); + const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0; + + // When using synchronous rendering (QtQuick) + // We are not sure that the frame queue is actually complete + // Since a call to render may not be synched with the completions + // of the RenderViewJobs + // In such a case we return early, waiting for a next call with + // the frame queue complete at this point + + // RenderQueue is complete (but that means it may be of size 0) + if (canSubmit && (queueIsComplete && !queueIsEmpty)) { + const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue(); + QTaskLogger submissionStatsPart1(m_services->systemInformation(), + {JobTypes::FrameSubmissionPart1, 0}, + QTaskLogger::Submission); + QTaskLogger submissionStatsPart2(m_services->systemInformation(), + {JobTypes::FrameSubmissionPart2, 0}, + QTaskLogger::Submission); + + if (canRender()) { + { // Scoped to destroy surfaceLock + QSurface *surface = nullptr; + for (const Render::RenderView *rv: renderViews) { + surface = rv->surface(); + if (surface) + break; + } + + SurfaceLocker surfaceLock(surface); + const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid()); + if (surfaceIsValid) { + // Reset state for each draw if we don't have complete control of the context + if (!m_ownedContext) + m_submissionContext->setCurrentStateSet(nullptr); + beganDrawing = m_submissionContext->beginDrawing(surface); + if (beganDrawing) { + // 1) Execute commands for buffer uploads, texture updates, shader loading first + updateGLResources(); + // 2) Update VAO and copy data into commands to allow concurrent submission + prepareCommandsSubmission(renderViews); + preprocessingComplete = true; + + // Purge shader which aren't used any longer + static int callCount = 0; + ++callCount; + const int shaderPurgePeriod = 600; + if (callCount % shaderPurgePeriod == 0) + m_glResourceManagers->glShaderManager()->purge(); + } + } + } + // 2) Proceed to next frame and start preparing frame n + 1 + m_renderQueue->reset(); + locker.unlock(); // Done protecting RenderQueue + m_vsyncFrameAdvanceService->proceedToNextFrame(); + hasCleanedQueueAndProceeded = true; + + // Only try to submit the RenderViews if the preprocessing was successful + // This part of the submission is happening in parallel to the RV building for the next frame + if (preprocessingComplete) { + submissionStatsPart1.end(submissionStatsPart2.restart()); + + // 3) Submit the render commands for frame n (making sure we never reference something that could be changing) + // Render using current device state and renderer configuration + submissionData = submitRenderViews(renderViews); + + // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader deleted...) + cleanGraphicsResources(); + } + } + + // Execute the pending shell commands + m_commandExecuter->performAsynchronousCommandExecution(renderViews); + + // Delete all the RenderViews which will clear the allocators + // that were used for their allocation + qDeleteAll(renderViews); + } + + // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong + // with the rendering and/or the renderqueue is incomplete from some reason + // or alternatively it could be complete but empty (RenderQueue of size 0) + + if (!hasCleanedQueueAndProceeded) { + // RenderQueue was full but something bad happened when + // trying to render it and therefore proceedToNextFrame was not called + // Note: in this case the renderQueue mutex is still locked + + // Reset the m_renderQueue so that we won't try to render + // with a queue used by a previous frame with corrupted content + // if the current queue was correctly submitted + m_renderQueue->reset(); + + // We allow the RenderTickClock service to proceed to the next frame + // In turn this will allow the aspect manager to request a new set of jobs + // to be performed for each aspect + m_vsyncFrameAdvanceService->proceedToNextFrame(); + } + + // Perform the last swapBuffers calls after the proceedToNextFrame + // as this allows us to gain a bit of time for the preparation of the + // next frame + // Finish up with last surface used in the list of RenderViews + if (beganDrawing) { + SurfaceLocker surfaceLock(submissionData.surface); + // Finish up with last surface used in the list of RenderViews + const bool swapBuffers = submissionData.lastBoundFBOId == m_submissionContext->defaultFBO() + && surfaceLock.isSurfaceValid() + && m_shouldSwapBuffers; + m_submissionContext->endDrawing(swapBuffers); + } +} + +// Called by RenderViewJobs +// When the frameQueue is complete and we are using a renderThread +// we allow the render thread to proceed +void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder) +{ + QMutexLocker locker(m_renderQueue->mutex()); // Prevent out of order execution + // We cannot use a lock free primitive here because: + // - QVector is not thread safe + // - Even if the insert is made correctly, the isFrameComplete call + // could be invalid since depending on the order of execution + // the counter could be complete but the renderview not yet added to the + // buffer depending on whichever order the cpu decides to process this + const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder); + locker.unlock(); // We're done protecting the queue at this point + if (isQueueComplete) { + if (m_renderThread && m_running.loadRelaxed()) + Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0); + m_submitRenderViewsSemaphore.release(1); + } +} + +bool Renderer::canRender() const + +{ + // Make sure that we've not been told to terminate + if (m_renderThread && !m_running.loadRelaxed()) { + qCDebug(Rendering) << "RenderThread termination requested whilst waiting"; + return false; + } + + // TO DO: Check if all surfaces have been destroyed... + // It may be better if the last window to be closed trigger a call to shutdown + // Rather than having checks for the surface everywhere + + return true; +} + +bool Renderer::isReadyToSubmit() +{ + // Make sure that we've been told to render before rendering + // Prevent ouf of order execution + m_submitRenderViewsSemaphore.acquire(1); + + // Check if shutdown has been requested + if (m_running.loadRelaxed() == 0) + return false; + + // The semaphore should only + // be released when the frame queue is complete and there's + // something to render + // The case of shutdown should have been handled just before + Q_ASSERT(m_renderQueue->isFrameQueueComplete()); + return true; +} + +// Main thread +QVariant Renderer::executeCommand(const QStringList &args) +{ + return m_commandExecuter->executeCommand(args); +} + +/*! + \internal + Called in the context of the aspect thread from QRenderAspect::onRegistered +*/ +void Renderer::setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) +{ + QMutexLocker locker(&m_offscreenSurfaceMutex); + m_offscreenHelper = helper; +} + +QSurfaceFormat Renderer::format() +{ + return m_format; +} + +// When this function is called, we must not be processing the commands for frame n+1 +void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderViews) +{ + OpenGLVertexArrayObject *vao = nullptr; + QHash<HVao, bool> updatedTable; + + for (RenderView *rv: renderViews) { + QVector<RenderCommand> &commands = rv->commands(); + for (RenderCommand &command : commands) { + // Update/Create VAO + if (command.m_type == RenderCommand::Draw) { + Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry); + GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer); + GLShader *shader = command.m_glShader; + + // We should never have inserted a command for which these are null + // in the first place + Q_ASSERT(rGeometry && rGeometryRenderer && shader); + + // The VAO should be created only once for a QGeometry and a ShaderProgram + // Manager should have a VAO Manager that are indexed by QMeshData and Shader + // RenderCommand should have a handle to the corresponding VAO for the Mesh and Shader + HVao vaoHandle; + + // Create VAO or return already created instance associated with command shader/geometry + // (VAO is emulated if not supported) + createOrUpdateVAO(&command, &vaoHandle, &vao); + command.m_vao = vaoHandle; + + // Avoids redoing the same thing for the same VAO + if (!updatedTable.contains(vaoHandle)) { + updatedTable.insert(vaoHandle, true); + + // Do we have any attributes that are dirty ? + const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, &command); + + // If true, we need to reupload all attributes to set the VAO + // Otherwise only dirty attributes will be updates + const bool requiresFullVAOUpdate = (!vao->isSpecified()) || (rGeometry->isDirty() || rGeometryRenderer->isDirty()); + + // Append dirty Geometry to temporary vector + // so that its dirtiness can be unset later + if (rGeometry->isDirty()) + m_dirtyGeometry.push_back(rGeometry); + + if (!command.m_activeAttributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { + Profiling::GLTimeRecorder recorder(Profiling::VAOUpload); + // Activate shader + m_submissionContext->activateShader(shader); + // Bind VAO + vao->bind(); + // Update or set Attributes and Buffers for the given rGeometry and Command + // Note: this fills m_dirtyAttributes as well + if (updateVAOWithAttributes(rGeometry, &command, shader, requiresFullVAOUpdate)) + vao->setSpecified(true); + } + } + + // Unset dirtiness on rGeometryRenderer only + // The rGeometry may be shared by several rGeometryRenderer + // so we cannot unset its dirtiness at this point + if (rGeometryRenderer->isDirty()) + rGeometryRenderer->unsetDirty(); + + // Prepare the ShaderParameterPack based on the active uniforms of the shader + shader->prepareUniforms(command.m_parameterPack); + + } else if (command.m_type == RenderCommand::Compute) { + GLShader *shader = command.m_glShader; + Q_ASSERT(shader); + + // Prepare the ShaderParameterPack based on the active uniforms of the shader + shader->prepareUniforms(command.m_parameterPack); + } + } + } + + // Make sure we leave nothing bound + if (vao) + vao->release(); + + // Unset dirtiness on Geometry and Attributes + // Note: we cannot do it in the loop above as we want to be sure that all + // the VAO which reference the geometry/attributes are properly updated + for (Attribute *attribute : qAsConst(m_dirtyAttributes)) + attribute->unsetDirty(); + m_dirtyAttributes.clear(); + + for (Geometry *geometry : qAsConst(m_dirtyGeometry)) + geometry->unsetDirty(); + m_dirtyGeometry.clear(); +} + +// Executed in a job +void Renderer::lookForAbandonedVaos() +{ + const QVector<HVao> activeVaos = m_glResourceManagers->vaoManager()->activeHandles(); + for (HVao handle : activeVaos) { + OpenGLVertexArrayObject *vao = m_glResourceManagers->vaoManager()->data(handle); + + // Make sure to only mark VAOs for deletion that were already created + // (ignore those that might be currently under construction in the render thread) + if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_glResourceManagers->glShaderManager())) { + m_abandonedVaosMutex.lock(); + m_abandonedVaos.push_back(handle); + m_abandonedVaosMutex.unlock(); + } + } +} + +// Executed in a job +void Renderer::lookForDirtyBuffers() +{ + const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles(); + for (const HBuffer &handle: activeBufferHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + if (buffer->isDirty()) + m_dirtyBuffers.push_back(handle); + } +} + +// Called in prepareSubmission +void Renderer::lookForDownloadableBuffers() +{ + m_downloadableBuffers.clear(); + const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles(); + for (const HBuffer &handle : activeBufferHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + if (buffer->access() & QBuffer::Read) + m_downloadableBuffers.push_back(buffer->peerId()); + } +} + +// Executed in a job +void Renderer::lookForDirtyTextures() +{ + // To avoid having Texture or TextureImage maintain relationships between + // one another, we instead perform a lookup here to check if a texture + // image has been updated to then notify textures referencing the image + // that they need to be updated + TextureImageManager *imageManager = m_nodesManager->textureImageManager(); + const QVector<HTextureImage> activeTextureImageHandles = imageManager->activeHandles(); + Qt3DCore::QNodeIdVector dirtyImageIds; + for (const HTextureImage &handle: activeTextureImageHandles) { + TextureImage *image = imageManager->data(handle); + if (image->isDirty()) { + dirtyImageIds.push_back(image->peerId()); + image->unsetDirty(); + } + } + + TextureManager *textureManager = m_nodesManager->textureManager(); + const QVector<HTexture> activeTextureHandles = textureManager->activeHandles(); + for (const HTexture &handle: activeTextureHandles) { + Texture *texture = textureManager->data(handle); + const QNodeIdVector imageIds = texture->textureImageIds(); + + // Does the texture reference any of the dirty texture images? + for (const QNodeId imageId: imageIds) { + if (dirtyImageIds.contains(imageId)) { + texture->addDirtyFlag(Texture::DirtyImageGenerators); + break; + } + } + + // Dirty meaning that something has changed on the texture + // either properties, parameters, shared texture id, generator or a texture image + if (texture->dirtyFlags() != Texture::NotDirty) + m_dirtyTextures.push_back(handle); + // Note: texture dirty flags are reset when actually updating the + // textures in updateGLResources() as resetting flags here would make + // us lose information about what was dirty exactly. + } +} + +// Executed in a job +void Renderer::reloadDirtyShaders() +{ + Q_ASSERT(isRunning()); + const QVector<HTechnique> activeTechniques = m_nodesManager->techniqueManager()->activeHandles(); + const QVector<HShaderBuilder> activeBuilders = m_nodesManager->shaderBuilderManager()->activeHandles(); + for (const HTechnique &techniqueHandle : activeTechniques) { + Technique *technique = m_nodesManager->techniqueManager()->data(techniqueHandle); + // If api of the renderer matches the one from the technique + if (technique->isCompatibleWithRenderer()) { + const auto passIds = technique->renderPasses(); + for (const QNodeId &passId : passIds) { + RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId); + HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram()); + Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle); + + ShaderBuilder *shaderBuilder = nullptr; + for (const HShaderBuilder &builderHandle : activeBuilders) { + ShaderBuilder *builder = m_nodesManager->shaderBuilderManager()->data(builderHandle); + if (builder->shaderProgramId() == shader->peerId()) { + shaderBuilder = builder; + break; + } + } + + if (shaderBuilder) { + shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter()); + + for (int i = 0; i <= QShaderProgram::Compute; i++) { + const auto shaderType = static_cast<QShaderProgram::ShaderType>(i); + if (!shaderBuilder->shaderGraph(shaderType).isValid()) + continue; + + if (shaderBuilder->isShaderCodeDirty(shaderType)) { + shaderBuilder->generateCode(shaderType); + m_shaderBuilderUpdates.append(shaderBuilder->takePendingUpdates()); + } + + const auto code = shaderBuilder->shaderCode(shaderType); + shader->setShaderCode(shaderType, code); + } + } + + if (shader != nullptr && shader->isDirty()) + loadShader(shader, shaderHandle); + } + } + } +} + +// Executed in job (in main thread when jobs are done) +void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager) +{ + Q_ASSERT(isRunning()); + + // Sync Shader + const QVector<HShader> activeShaders = m_nodesManager->shaderManager()->activeHandles(); + for (const HShader &handle :activeShaders) { + Shader *s = m_nodesManager->shaderManager()->data(handle); + if (s->requiresFrontendSync()) { + QShaderProgram *frontend = static_cast<decltype(frontend)>(manager->lookupNode(s->peerId())); + QShaderProgramPrivate *dFrontend = static_cast<decltype(dFrontend)>(QNodePrivate::get(frontend)); + s->unsetRequiresFrontendSync(); + dFrontend->setStatus(s->status()); + dFrontend->setLog(s->log()); + } + } + + // Sync ShaderBuilder + const QVector<ShaderBuilderUpdate> shaderBuilderUpdates = std::move(m_shaderBuilderUpdates); + for (const ShaderBuilderUpdate &update : shaderBuilderUpdates) { + QShaderProgramBuilder *builder = static_cast<decltype(builder)>(manager->lookupNode(update.builderId)); + QShaderProgramBuilderPrivate *dBuilder = static_cast<decltype(dBuilder)>(QNodePrivate::get(builder)); + dBuilder->setShaderCode(update.shaderCode, update.shaderType); + } +} + +// Executed in a job (in main thread when jobs are done) +void Renderer::sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager) +{ + const QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> updateTextureProperties = std::move(m_updatedTextureProperties); + for (const auto &pair : updateTextureProperties) { + const Qt3DCore::QNodeIdVector targetIds = pair.second; + for (const Qt3DCore::QNodeId &targetId: targetIds) { + // Lookup texture + Texture *t = m_nodesManager->textureManager()->lookupResource(targetId); + // If backend texture is Dirty, some property has changed and the properties we are + // about to send are already outdate + if (t == nullptr || t->dirtyFlags() != Texture::NotDirty) + continue; + + QAbstractTexture *texture = static_cast<QAbstractTexture *>(manager->lookupNode(targetId)); + if (!texture) + continue; + const TextureProperties &properties = pair.first.properties; + + const bool blocked = texture->blockNotifications(true); + texture->setWidth(properties.width); + texture->setHeight(properties.height); + texture->setDepth(properties.depth); + texture->setLayers(properties.layers); + texture->setFormat(properties.format); + texture->blockNotifications(blocked); + + QAbstractTexturePrivate *dTexture = static_cast<QAbstractTexturePrivate *>(QNodePrivate::get(texture)); + + dTexture->setStatus(properties.status); + dTexture->setHandleType(pair.first.handleType); + dTexture->setHandle(pair.first.handle); + } + } +} + +// Executed in a job +void Renderer::sendSetFenceHandlesToFrontend() +{ + const QVector<QPair<Qt3DCore::QNodeId, GLFence>> updatedSetFence = std::move(m_updatedSetFences); + FrameGraphManager *fgManager = m_nodesManager->frameGraphManager(); + for (const auto &pair : updatedSetFence) { + FrameGraphNode *fgNode = fgManager->lookupNode(pair.first); + if (fgNode != nullptr) { // Node could have been deleted before we got a chance to notify it + Q_ASSERT(fgNode->nodeType() == FrameGraphNode::SetFence); + SetFence *setFenceNode = static_cast<SetFence *>(fgNode); + setFenceNode->setHandleType(QSetFence::OpenGLFenceId); + setFenceNode->setHandle(QVariant::fromValue(pair.second)); + } + } +} + +// Executed in a job (in main thread when jobs done) +void Renderer::sendDisablesToFrontend(Qt3DCore::QAspectManager *manager) +{ + // SubtreeEnabled + const auto updatedDisables = std::move(m_updatedDisableSubtreeEnablers); + for (const auto &nodeId : updatedDisables) { + QSubtreeEnabler *frontend = static_cast<decltype(frontend)>(manager->lookupNode(nodeId)); + frontend->setEnabled(false); + } + + // Compute Commands + const QVector<HComputeCommand> activeCommands = m_nodesManager->computeJobManager()->activeHandles(); + for (const HComputeCommand &handle :activeCommands) { + ComputeCommand *c = m_nodesManager->computeJobManager()->data(handle); + if (c->hasReachedFrameCount()) { + QComputeCommand *frontend = static_cast<decltype(frontend)>(manager->lookupNode(c->peerId())); + frontend->setEnabled(false); + c->resetHasReachedFrameCount(); + } + } +} + +// Render Thread (or QtQuick RenderThread when using Scene3D) +// Scene3D: When using Scene3D rendering, we can't assume that when +// updateGLResources is called, the resource handles points to still existing +// objects. This is because Scene3D calls doRender independently of whether all +// jobs have completed or not which in turn calls proceedToNextFrame under some +// conditions. Such conditions are usually met on startup to avoid deadlocks. +// proceedToNextFrame triggers the syncChanges calls for the next frame, which +// may contain destruction changes targeting resources. When the above +// happens, this can result in the dirtyResource vectors containing handles of +// objects that may already have been destroyed +void Renderer::updateGLResources() +{ + { + // Update active fence objects: + // - Destroy fences that have reached their signaled state + GLFenceManager *fenceManager = m_glResourceManagers->glFenceManager(); + const auto end = fenceManager->end(); + auto it = fenceManager->begin(); + while (it != end) { + const GLFence fence = it.value(); + if (m_submissionContext->wasSyncSignaled(fence)) { + // Fence was signaled, we delete it + // before removing the entry from the manager + m_submissionContext->deleteSync(fence); + it = fenceManager->erase(it); + } else { + ++it; + } + } + } + + { + Profiling::GLTimeRecorder recorder(Profiling::BufferUpload); + const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers); + for (const HBuffer &handle: dirtyBufferHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + + // Can be null when using Scene3D rendering + if (buffer == nullptr) + continue; + + // Forces creation if it doesn't exit + // Also note the binding point doesn't really matter here, we just upload data + if (!m_submissionContext->hasGLBufferForBuffer(buffer)) + m_submissionContext->glBufferForRenderBuffer(buffer); + // Update the glBuffer data + m_submissionContext->updateBuffer(buffer); + buffer->unsetDirty(); + } + } + +#ifndef SHADER_LOADING_IN_COMMAND_THREAD + { + Profiling::GLTimeRecorder recorder(Profiling::ShaderUpload); + const QVector<HShader> dirtyShaderHandles = std::move(m_dirtyShaders); + ShaderManager *shaderManager = m_nodesManager->shaderManager(); + for (const HShader &handle: dirtyShaderHandles) { + Shader *shader = shaderManager->data(handle); + + // Can be null when using Scene3D rendering + if (shader == nullptr) + continue; + + // Compile shader + m_submissionContext->loadShader(shader, shaderManager, m_glResourceManagers->glShaderManager()); + } + } +#endif + + { + Profiling::GLTimeRecorder recorder(Profiling::TextureUpload); + const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures); + for (const HTexture &handle: activeTextureHandles) { + Texture *texture = m_nodesManager->textureManager()->data(handle); + + // Can be null when using Scene3D rendering + if (texture == nullptr) + continue; + + // Create or Update GLTexture (the GLTexture instance is created if required + // and all things that can take place without a GL context are done here) + updateTexture(texture); + } + // We want to upload textures data at this point as the SubmissionThread and + // AspectThread are locked ensuring no races between Texture/TextureImage and + // GLTexture + if (m_submissionContext != nullptr) { + GLTextureManager *glTextureManager = m_glResourceManagers->glTextureManager(); + const QVector<HGLTexture> glTextureHandles = glTextureManager->activeHandles(); + // Upload texture data + for (const HGLTexture &glTextureHandle : glTextureHandles) { + GLTexture *glTexture = glTextureManager->data(glTextureHandle); + + // We create/update the actual GL texture using the GL context at this point + const GLTexture::TextureUpdateInfo info = glTexture->createOrUpdateGLTexture(); + + // GLTexture creation provides us width/height/format ... information + // for textures which had not initially specified these information (TargetAutomatic...) + // Gather these information and store them to be distributed by a change next frame + const QNodeIdVector referenceTextureIds = { glTextureManager->texNodeIdForGLTexture.value(glTexture) }; + // Store properties and referenceTextureIds + if (info.wasUpdated) { + Texture::TextureUpdateInfo updateInfo; + updateInfo.properties = info.properties; + updateInfo.handleType = QAbstractTexture::OpenGLTextureId; + updateInfo.handle = info.texture ? QVariant(info.texture->textureId()) : QVariant(); + m_updatedTextureProperties.push_back({updateInfo, referenceTextureIds}); + } + } + } + + // Record ids of texture to cleanup while we are still blocking the aspect thread + m_textureIdsToCleanup += m_nodesManager->textureManager()->takeTexturesIdsToCleanup(); + } + + // Record list of buffer that might need uploading + lookForDownloadableBuffers(); +} + +// Render Thread +void Renderer::updateTexture(Texture *texture) +{ + // Check that the current texture images are still in place, if not, do not update + const bool isValid = texture->isValid(m_nodesManager->textureImageManager()); + if (!isValid) { + qWarning() << Q_FUNC_INFO << "QTexture referencing invalid QTextureImages"; + return; + } + + // All textures are unique, if you instanciate twice the exact same texture + // this will create 2 identical GLTextures, no sharing will take place + + // Try to find the associated GLTexture for the backend Texture + GLTextureManager *glTextureManager = m_glResourceManagers->glTextureManager(); + GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId()); + + // No GLTexture associated yet -> create it + if (glTexture == nullptr) { + glTexture = glTextureManager->getOrCreateResource(texture->peerId()); + glTextureManager->texNodeIdForGLTexture.insert(glTexture, texture->peerId()); + } + + // Update GLTexture to match Texture instance + const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags(); + if (dirtyFlags.testFlag(Texture::DirtySharedTextureId)) + glTexture->setSharedTextureId(texture->sharedTextureId()); + + if (dirtyFlags.testFlag(Texture::DirtyProperties)) + glTexture->setProperties(texture->properties()); + + if (dirtyFlags.testFlag(Texture::DirtyParameters)) + glTexture->setParameters(texture->parameters()); + + // Will make the texture requestUpload + if (dirtyFlags.testFlag(Texture::DirtyImageGenerators)) { + const QNodeIdVector textureImageIds = texture->textureImageIds(); + QVector<GLTexture::Image> images; + images.reserve(textureImageIds.size()); + // TODO: Move this into GLTexture directly + for (const QNodeId textureImageId : textureImageIds) { + const TextureImage *img = m_nodesManager->textureImageManager()->lookupResource(textureImageId); + if (img == nullptr) { + qWarning() << Q_FUNC_INFO << "invalid TextureImage handle"; + } else { + GLTexture::Image glImg {img->dataGenerator(), img->layer(), img->mipLevel(), img->face()}; + images.push_back(glImg); + } + } + glTexture->setImages(images); + } + + // Will make the texture requestUpload + if (dirtyFlags.testFlag(Texture::DirtyDataGenerator)) + glTexture->setGenerator(texture->dataGenerator()); + + // Will make the texture requestUpload + if (dirtyFlags.testFlag(Texture::DirtyPendingDataUpdates)) + glTexture->addTextureDataUpdates(texture->takePendingTextureDataUpdates()); + + // Unset the dirty flag on the texture + texture->unsetDirty(); +} + +// Render Thread +void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId) +{ + GLTextureManager *glTextureManager = m_glResourceManagers->glTextureManager(); + GLTexture *glTexture = glTextureManager->lookupResource(cleanedUpTextureId); + + // Destroying the GLTexture implicitely also destroy the GL resources + if (glTexture != nullptr) { + glTextureManager->releaseResource(cleanedUpTextureId); + glTextureManager->texNodeIdForGLTexture.remove(glTexture); + } +} + +// Render Thread +void Renderer::cleanupShader(const Shader *shader) +{ + GLShaderManager *glShaderManager = m_glResourceManagers->glShaderManager(); + GLShader *glShader = glShaderManager->lookupResource(shader->peerId()); + + if (glShader != nullptr) + glShaderManager->abandon(glShader, shader); +} + +// Called by SubmitRenderView +void Renderer::downloadGLBuffers() +{ + const QVector<Qt3DCore::QNodeId> downloadableHandles = std::move(m_downloadableBuffers); + for (const Qt3DCore::QNodeId &bufferId : downloadableHandles) { + BufferManager *bufferManager = m_nodesManager->bufferManager(); + BufferManager::ReadLocker locker(const_cast<const BufferManager *>(bufferManager)); + Buffer *buffer = bufferManager->lookupResource(bufferId); + // Buffer could have been destroyed at this point + if (!buffer) + continue; + // locker is protecting us from the buffer being destroy while we're looking + // up its content + const QByteArray content = m_submissionContext->downloadBufferContent(buffer); + m_sendBufferCaptureJob->addRequest(QPair<Qt3DCore::QNodeId, QByteArray>(bufferId, content)); + } +} + +// Happens in RenderThread context when all RenderViewJobs are done +// Returns the id of the last bound FBO +Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Render::RenderView *> &renderViews) +{ + QElapsedTimer timer; + quint64 queueElapsed = 0; + timer.start(); + + const int renderViewsCount = renderViews.size(); + quint64 frameElapsed = queueElapsed; + m_lastFrameCorrect.storeRelaxed(1); // everything fine until now..... + + qCDebug(Memory) << Q_FUNC_INFO << "rendering frame "; + + // We might not want to render on the default FBO + uint lastBoundFBOId = m_submissionContext->boundFrameBufferObject(); + QSurface *surface = nullptr; + QSurface *previousSurface = nullptr; + for (const Render::RenderView *rv: renderViews) { + previousSurface = rv->surface(); + if (previousSurface) + break; + } + QSurface *lastUsedSurface = nullptr; + + for (int i = 0; i < renderViewsCount; ++i) { + // Initialize GraphicsContext for drawing + // If the RenderView has a RenderStateSet defined + const RenderView *renderView = renderViews.at(i); + + // Check if using the same surface as the previous RenderView. + // If not, we have to free up the context from the previous surface + // and make the context current on the new surface + surface = renderView->surface(); + SurfaceLocker surfaceLock(surface); + + // TO DO: Make sure that the surface we are rendering too has not been unset + + // For now, if we do not have a surface, skip this renderview + // TODO: Investigate if it's worth providing a fallback offscreen surface + // to use when surface is null. Or if we should instead expose an + // offscreensurface to Qt3D. + if (!surface || !surfaceLock.isSurfaceValid()) { + m_lastFrameCorrect.storeRelaxed(0); + continue; + } + + lastUsedSurface = surface; + const bool surfaceHasChanged = surface != previousSurface; + + if (surfaceHasChanged && previousSurface) { + const bool swapBuffers = lastBoundFBOId == m_submissionContext->defaultFBO() + && surfaceLock.isSurfaceValid() + && m_shouldSwapBuffers; + // We only call swap buffer if we are sure the previous surface is still valid + m_submissionContext->endDrawing(swapBuffers); + } + + if (surfaceHasChanged) { + // If we can't make the context current on the surface, skip to the + // next RenderView. We won't get the full frame but we may get something + if (!m_submissionContext->beginDrawing(surface)) { + qWarning() << "Failed to make OpenGL context current on surface"; + m_lastFrameCorrect.storeRelaxed(0); + continue; + } + + previousSurface = surface; + lastBoundFBOId = m_submissionContext->boundFrameBufferObject(); + } + + // Apply Memory Barrier if needed + if (renderView->memoryBarrier() != QMemoryBarrier::None) + m_submissionContext->memoryBarrier(renderView->memoryBarrier()); + + + // Insert Fence into command stream if needed + const Qt3DCore::QNodeIdVector insertFenceIds = renderView->insertFenceIds(); + GLFenceManager *fenceManager = m_glResourceManagers->glFenceManager(); + for (const Qt3DCore::QNodeId insertFenceId : insertFenceIds) { + // If the fence is not in the manager, then it hasn't been inserted + // into the command stream yet. + if (fenceManager->find(insertFenceId) == fenceManager->end()) { + // Insert fence into command stream + GLFence glFence = m_submissionContext->fenceSync(); + // Record glFence + fenceManager->insert(insertFenceId, glFence); + // Add entry for notification changes to be sent + m_updatedSetFences.push_back({insertFenceId, glFence}); + } + // If it is in the manager, then it hasn't been signaled yet, + // nothing we can do but try at the next frame + } + + // Wait for fences if needed + const QVector<QWaitFenceData> waitFences = renderView->waitFences(); + for (const QWaitFenceData &waitFence : waitFences) { + // TO DO + if (waitFence.handleType != QWaitFence::OpenGLFenceId) { + qWarning() << "WaitFence handleType should be OpenGLFenceId when using the Qt 3D OpenGL renderer"; + continue; + } + GLFence fence = reinterpret_cast<GLFence>(waitFence.handle.value<qintptr>()); + if (fence == nullptr) + continue; + + if (waitFence.waitOnCPU) { + m_submissionContext->clientWaitSync(fence, + waitFence.timeout); + } else { + m_submissionContext->waitSync(fence); + } + } + + // Note: the RenderStateSet is allocated once per RV if needed + // and it contains a list of StateVariant value types + RenderStateSet *renderViewStateSet = renderView->stateSet(); + + { + Profiling::GLTimeRecorder recorder(Profiling::StateUpdate); + // Set the RV state if not null, + if (renderViewStateSet != nullptr) + m_submissionContext->setCurrentStateSet(renderViewStateSet); + else + m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet); + } + + // Set RenderTarget ... + // Activate RenderTarget + { + Profiling::GLTimeRecorder recorder(Profiling::RenderTargetUpdate); + m_submissionContext->activateRenderTarget(renderView->renderTargetId(), + renderView->attachmentPack(), + lastBoundFBOId); + } + + { + Profiling::GLTimeRecorder recorder(Profiling::ClearBuffer); + // set color, depth, stencil clear values (only if needed) + auto clearBufferTypes = renderView->clearTypes(); + if (clearBufferTypes & QClearBuffers::ColorBuffer) { + const QVector4D vCol = renderView->globalClearColorBufferInfo().clearColor; + m_submissionContext->clearColor(QColor::fromRgbF(vCol.x(), vCol.y(), vCol.z(), vCol.w())); + } + if (clearBufferTypes & QClearBuffers::DepthBuffer) + m_submissionContext->clearDepthValue(renderView->clearDepthValue()); + if (clearBufferTypes & QClearBuffers::StencilBuffer) + m_submissionContext->clearStencilValue(renderView->clearStencilValue()); + + // Clear BackBuffer + m_submissionContext->clearBackBuffer(clearBufferTypes); + + // if there are ClearColors set for different draw buffers, + // clear each of these draw buffers individually now + const QVector<ClearBufferInfo> clearDrawBuffers = renderView->specificClearColorBufferInfo(); + for (const ClearBufferInfo &clearBuffer : clearDrawBuffers) + m_submissionContext->clearBufferf(clearBuffer.drawBufferIndex, clearBuffer.clearColor); + } + + // Set the Viewport + m_submissionContext->setViewport(renderView->viewport(), renderView->surfaceSize()); + + // Execute the render commands + if (!executeCommandsSubmission(renderView)) + m_lastFrameCorrect.storeRelaxed(0); // something went wrong; make sure to render the next frame! + + // executeCommandsSubmission takes care of restoring the stateset to the value + // of gc->currentContext() at the moment it was called (either + // renderViewStateSet or m_defaultRenderStateSet) + if (!renderView->renderCaptureNodeId().isNull()) { + const QRenderCaptureRequest request = renderView->renderCaptureRequest(); + const QSize size = m_submissionContext->renderTargetSize(renderView->surfaceSize()); + QRect rect(QPoint(0, 0), size); + if (!request.rect.isEmpty()) + rect = rect.intersected(request.rect); + QImage image; + if (!rect.isEmpty()) { + // Bind fbo as read framebuffer + m_submissionContext->bindFramebuffer(m_submissionContext->activeFBO(), GraphicsHelperInterface::FBORead); + image = m_submissionContext->readFramebuffer(rect); + } else { + qWarning() << "Requested capture rectangle is outside framebuffer"; + } + Render::RenderCapture *renderCapture = + static_cast<Render::RenderCapture*>(m_nodesManager->frameGraphManager()->lookupNode(renderView->renderCaptureNodeId())); + renderCapture->addRenderCapture(request.captureId, image); + if (!m_pendingRenderCaptureSendRequests.contains(renderView->renderCaptureNodeId())) + m_pendingRenderCaptureSendRequests.push_back(renderView->renderCaptureNodeId()); + } + + if (renderView->isDownloadBuffersEnable()) + downloadGLBuffers(); + + // Perform BlitFramebuffer operations + if (renderView->hasBlitFramebufferInfo()) { + const auto &blitFramebufferInfo = renderView->blitFrameBufferInfo(); + const QNodeId inputTargetId = blitFramebufferInfo.sourceRenderTargetId; + const QNodeId outputTargetId = blitFramebufferInfo.destinationRenderTargetId; + const QRect inputRect = blitFramebufferInfo.sourceRect; + const QRect outputRect = blitFramebufferInfo.destinationRect; + const QRenderTargetOutput::AttachmentPoint inputAttachmentPoint = blitFramebufferInfo.sourceAttachmentPoint; + const QRenderTargetOutput::AttachmentPoint outputAttachmentPoint = blitFramebufferInfo.destinationAttachmentPoint; + const QBlitFramebuffer::InterpolationMethod interpolationMethod = blitFramebufferInfo.interpolationMethod; + m_submissionContext->blitFramebuffer(inputTargetId, outputTargetId, inputRect, outputRect, lastBoundFBOId, + inputAttachmentPoint, outputAttachmentPoint, + interpolationMethod); + } + + + frameElapsed = timer.elapsed() - frameElapsed; + qCDebug(Rendering) << Q_FUNC_INFO << "Submitted Renderview " << i + 1 << "/" << renderViewsCount << "in " << frameElapsed << "ms"; + frameElapsed = timer.elapsed(); + } + + // Bind lastBoundFBOId back. Needed also in threaded mode. + // lastBoundFBOId != m_graphicsContext->activeFBO() when the last FrameGraph leaf node/renderView + // contains RenderTargetSelector/RenderTarget + if (lastBoundFBOId != m_submissionContext->activeFBO()) + m_submissionContext->bindFramebuffer(lastBoundFBOId, GraphicsHelperInterface::FBOReadAndDraw); + + // Reset state and call doneCurrent if the surface + // is valid and was actually activated + if (lastUsedSurface && m_submissionContext->hasValidGLHelper()) { + // Reset state to the default state if the last stateset is not the + // defaultRenderStateSet + if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet) + m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet); + } + + queueElapsed = timer.elapsed() - queueElapsed; + qCDebug(Rendering) << Q_FUNC_INFO << "Submission of Queue in " << queueElapsed << "ms <=> " << queueElapsed / renderViewsCount << "ms per RenderView <=> Avg " << 1000.0f / (queueElapsed * 1.0f/ renderViewsCount * 1.0f) << " RenderView/s"; + qCDebug(Rendering) << Q_FUNC_INFO << "Submission Completed in " << timer.elapsed() << "ms"; + + // Stores the necessary information to safely perform + // the last swap buffer call + ViewSubmissionResultData resultData; + resultData.lastBoundFBOId = lastBoundFBOId; + resultData.surface = lastUsedSurface; + return resultData; +} + +void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node) +{ + Q_UNUSED(node) + m_dirtyBits.marked |= changes; +} + +Renderer::BackendNodeDirtySet Renderer::dirtyBits() +{ + return m_dirtyBits.marked; +} + +#if defined(QT_BUILD_INTERNAL) +void Renderer::clearDirtyBits(BackendNodeDirtySet changes) +{ + m_dirtyBits.remaining &= ~changes; + m_dirtyBits.marked &= ~changes; +} +#endif + +bool Renderer::shouldRender() const +{ + // Only render if something changed during the last frame, or the last frame + // was not rendered successfully (or render-on-demand is disabled) + return (m_settings->renderPolicy() == QRenderSettings::Always + || m_dirtyBits.marked != 0 + || m_dirtyBits.remaining != 0 + || !m_lastFrameCorrect.loadRelaxed()); +} + +void Renderer::skipNextFrame() +{ + Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always); + + // make submitRenderViews() actually run + m_renderQueue->setNoRender(); + m_submitRenderViewsSemaphore.release(1); +} + +void Renderer::jobsDone(Qt3DCore::QAspectManager *manager) +{ + // called in main thread once all jobs are done running + + // sync captured renders to frontend + const QVector<Qt3DCore::QNodeId> pendingCaptureIds = std::move(m_pendingRenderCaptureSendRequests); + for (const Qt3DCore::QNodeId &id : qAsConst(pendingCaptureIds)) { + auto *backend = static_cast<Qt3DRender::Render::RenderCapture *> + (m_nodesManager->frameGraphManager()->lookupNode(id)); + backend->syncRenderCapturesToFrontend(manager); + } + + // Do we need to notify any texture about property changes? + if (m_updatedTextureProperties.size() > 0) + sendTextureChangesToFrontend(manager); + + sendDisablesToFrontend(manager); +} + +// Jobs we may have to run even if no rendering will happen +QVector<QAspectJobPtr> Renderer::preRenderingJobs() +{ + QVector<QAspectJobPtr> jobs; + + // Do we need to notify frontend about fence change? + if (m_updatedSetFences.size() > 0) + jobs.push_back(m_sendSetFenceHandlesToFrontendJob); + + if (m_sendBufferCaptureJob->hasRequests()) + jobs.push_back(m_sendBufferCaptureJob); + + jobs.append(pickBoundingVolumeJob()); + jobs.append(rayCastingJob()); + + return jobs; +} + +// Waits to be told to create jobs for the next frame +// Called by QRenderAspect jobsToExecute context of QAspectThread +// Returns all the jobs (and with proper dependency chain) required +// for the rendering of the scene +QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() +{ + QVector<QAspectJobPtr> renderBinJobs; + // Create the jobs to build the frame + const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs(); + + // Remove previous dependencies + m_calculateBoundingVolumeJob->removeDependency(QWeakPointer<QAspectJob>()); + m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>()); + + // Set dependencies + for (const QAspectJobPtr &bufferJob : bufferJobs) + m_calculateBoundingVolumeJob->addDependency(bufferJob); + + m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot()); + + const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining; + m_dirtyBits.marked = {}; + m_dirtyBits.remaining = {}; + BackendNodeDirtySet notCleared = {}; + + // Add jobs + const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty; + if (entitiesEnabledDirty) { + renderBinJobs.push_back(m_updateTreeEnabledJob); + // This dependency is added here because we clear all dependencies + // at the start of this function. + m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob); + } + + if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) { + renderBinJobs.push_back(m_worldTransformJob); + renderBinJobs.push_back(m_updateWorldBoundingVolumeJob); + renderBinJobs.push_back(m_updateShaderDataTransformJob); + } + + if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty || + dirtyBitsForFrame & AbstractRenderer::BuffersDirty) { + renderBinJobs.push_back(m_calculateBoundingVolumeJob); + renderBinJobs.push_back(m_updateMeshTriangleListJob); + } + + if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty || + dirtyBitsForFrame & AbstractRenderer::TransformDirty) { + renderBinJobs.push_back(m_expandBoundingVolumeJob); + } + + // TO DO: Conditionally add if skeletons dirty + renderBinJobs.push_back(m_syncLoadingJobs); + m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints()); + renderBinJobs.push_back(m_updateSkinningPaletteJob); + renderBinJobs.push_back(m_updateLevelOfDetailJob); + renderBinJobs.push_back(m_cleanupJob); + renderBinJobs.append(bufferJobs); + + // Jobs to prepare GL Resource upload + renderBinJobs.push_back(m_vaoGathererJob); + + if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty) + renderBinJobs.push_back(m_bufferGathererJob); + + if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty) + renderBinJobs.push_back(m_textureGathererJob); + + + // Layer cache is dependent on layers, layer filters (hence FG structure + // changes) and the enabled flag on entities + const bool frameGraphDirty = dirtyBitsForFrame & AbstractRenderer::FrameGraphDirty; + const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty; + const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty || frameGraphDirty; + const bool shadersDirty = dirtyBitsForFrame & AbstractRenderer::ShadersDirty; + const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty; + const bool lightsDirty = dirtyBitsForFrame & AbstractRenderer::LightsDirty; + const bool computeableDirty = dirtyBitsForFrame & AbstractRenderer::ComputeDirty; + const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty; + const bool materialCacheNeedsToBeRebuilt = shadersDirty || materialDirty || frameGraphDirty; + const bool renderCommandsDirty = materialCacheNeedsToBeRebuilt || renderableDirty || computeableDirty; + + // Rebuild Entity Layers list if layers are dirty + if (layersDirty) + renderBinJobs.push_back(m_updateEntityLayersJob); + + if (renderableDirty) { + renderBinJobs.push_back(m_renderableEntityFilterJob); + renderBinJobs.push_back(m_cacheRenderableEntitiesJob); + } + + if (computeableDirty) { + renderBinJobs.push_back(m_computableEntityFilterJob); + renderBinJobs.push_back(m_cacheComputableEntitiesJob); + } + + if (lightsDirty) { + renderBinJobs.push_back(m_lightGathererJob); + renderBinJobs.push_back(m_cacheLightsJob); + } + + QMutexLocker lock(m_renderQueue->mutex()); + if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case) + // Traverse the current framegraph. For each leaf node create a + // RenderView and set its configuration then create a job to + // populate the RenderView with a set of RenderCommands that get + // their details from the RenderNodes that are visible to the + // Camera selected by the framegraph configuration + if (frameGraphDirty) { + FrameGraphVisitor visitor(m_nodesManager->frameGraphManager()); + m_frameGraphLeaves = visitor.traverse(frameGraphRoot()); + // Remove leaf nodes that no longer exist from cache + const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys(); + for (FrameGraphNode *leafNode : keys) { + if (!m_frameGraphLeaves.contains(leafNode)) + m_cache.leafNodeCache.remove(leafNode); + } + + // Handle single shot subtree enablers + const auto subtreeEnablers = visitor.takeEnablersToDisable(); + for (auto *node : subtreeEnablers) + m_updatedDisableSubtreeEnablers.push_back(node->peerId()); + } + + const int fgBranchCount = m_frameGraphLeaves.size(); + for (int i = 0; i < fgBranchCount; ++i) { + FrameGraphNode *leaf = m_frameGraphLeaves.at(i); + RenderViewBuilder builder(leaf, i, this); + // If we have a new RV (wasn't in the cache before, then it contains no cached data) + const bool isNewRV = !m_cache.leafNodeCache.contains(leaf); + builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt || isNewRV); + builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt || isNewRV); + builder.setRenderCommandCacheNeedsToBeRebuilt(renderCommandsDirty || isNewRV); + + builder.prepareJobs(); + renderBinJobs.append(builder.buildJobHierachy()); + } + + // Set target number of RenderViews + m_renderQueue->setTargetRenderViewCount(fgBranchCount); + } else { + // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later + // if none of those jobs are started this frame + notCleared |= AbstractRenderer::EntityEnabledDirty; + notCleared |= AbstractRenderer::FrameGraphDirty; + notCleared |= AbstractRenderer::LayersDirty; + } + + if (isRunning() && m_submissionContext->isInitialized()) { + if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty ) + renderBinJobs.push_back(m_filterCompatibleTechniqueJob); + if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty) + renderBinJobs.push_back(m_introspectShaderJob); + } else { + notCleared |= AbstractRenderer::TechniquesDirty; + notCleared |= AbstractRenderer::ShadersDirty; + } + + m_dirtyBits.remaining = dirtyBitsForFrame & notCleared; + + return renderBinJobs; +} + +QAspectJobPtr Renderer::pickBoundingVolumeJob() +{ + // Set values on pickBoundingVolumeJob + RenderSettings *renderSetting = settings(); + if (renderSetting != nullptr) { + m_pickBoundingVolumeJob->setRenderSettings(renderSetting); + m_pickBoundingVolumeJob->setFrameGraphRoot(frameGraphRoot()); + m_pickBoundingVolumeJob->setMouseEvents(pendingPickingEvents()); + m_pickBoundingVolumeJob->setKeyEvents(pendingKeyEvents()); + } + + return m_pickBoundingVolumeJob; +} + +QAspectJobPtr Renderer::rayCastingJob() +{ + // Set values on rayCastingJob + RenderSettings *renderSetting = settings(); + if (renderSetting != nullptr) { + m_rayCastingJob->setRenderSettings(renderSetting); + m_rayCastingJob->setFrameGraphRoot(frameGraphRoot()); + } + + return m_rayCastingJob; +} + +QAspectJobPtr Renderer::syncLoadingJobs() +{ + return m_syncLoadingJobs; +} + +QAspectJobPtr Renderer::expandBoundingVolumeJob() +{ + return m_expandBoundingVolumeJob; +} + +QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const +{ + return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data()); +} + +// Called by executeCommands +void Renderer::performDraw(RenderCommand *command) +{ + // Indirect Draw Calls + if (command->m_drawIndirect) { + + // Bind the indirect draw buffer + Buffer *indirectDrawBuffer = m_nodesManager->bufferManager()->data(command->m_indirectDrawBuffer); + if (Q_UNLIKELY(indirectDrawBuffer == nullptr)) { + qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve Buffer"; + return; + } + + // Get GLBuffer from Buffer; + GLBuffer *indirectDrawGLBuffer = m_submissionContext->glBufferForRenderBuffer(indirectDrawBuffer); + if (Q_UNLIKELY(indirectDrawGLBuffer == nullptr)) { + qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve GLBuffer"; + return; + } + + // Bind GLBuffer + const bool successfullyBound = indirectDrawGLBuffer->bind(m_submissionContext.data(), GLBuffer::DrawIndirectBuffer); + + if (Q_LIKELY(successfullyBound)) { + // TO DO: Handle multi draw variants if attribute count > 1 + if (command->m_drawIndexed) { + m_submissionContext->drawElementsIndirect(command->m_primitiveType, + command->m_indexAttributeDataType, + reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset))); + } else { + m_submissionContext->drawArraysIndirect(command->m_primitiveType, + reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset))); + } + } else { + qWarning() << "Failed to bind IndirectDrawBuffer"; + } + + } else { // Direct Draw Calls + + // TO DO: Add glMulti Draw variants + if (command->m_primitiveType == QGeometryRenderer::Patches) + m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch); + + if (command->m_primitiveRestartEnabled) + m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue); + + // TO DO: Add glMulti Draw variants + if (command->m_drawIndexed) { + Profiling::GLTimeRecorder recorder(Profiling::DrawElement); + m_submissionContext->drawElementsInstancedBaseVertexBaseInstance(command->m_primitiveType, + command->m_primitiveCount, + command->m_indexAttributeDataType, + reinterpret_cast<void*>(quintptr(command->m_indexAttributeByteOffset)), + command->m_instanceCount, + command->m_indexOffset, + command->m_firstInstance); + } else { + Profiling::GLTimeRecorder recorder(Profiling::DrawArray); + m_submissionContext->drawArraysInstancedBaseInstance(command->m_primitiveType, + command->m_firstVertex, + command->m_primitiveCount, + command->m_instanceCount, + command->m_firstInstance); + } + } + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + int err = m_submissionContext->openGLContext()->functions()->glGetError(); + if (err) + qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16); +#endif + + if (command->m_primitiveRestartEnabled) + m_submissionContext->disablePrimitiveRestart(); +} + +void Renderer::performCompute(const RenderView *, RenderCommand *command) +{ + { + Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); + GLShader *shader = m_glResourceManagers->glShaderManager()->lookupResource(command->m_shaderId); + m_submissionContext->activateShader(shader); + } + { + Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); + m_submissionContext->setParameters(command->m_parameterPack); + } + { + Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute); + m_submissionContext->dispatchCompute(command->m_workGroups[0], + command->m_workGroups[1], + command->m_workGroups[2]); + } + // HACK: Reset the compute flag to dirty + m_dirtyBits.marked |= AbstractRenderer::ComputeDirty; + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + int err = m_submissionContext->openGLContext()->functions()->glGetError(); + if (err) + qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16); +#endif +} + +void Renderer::createOrUpdateVAO(RenderCommand *command, + HVao *previousVaoHandle, + OpenGLVertexArrayObject **vao) +{ + const VAOIdentifier vaoKey(command->m_geometry, command->m_shaderId); + + VAOManager *vaoManager = m_glResourceManagers->vaoManager(); + command->m_vao = vaoManager->lookupHandle(vaoKey); + + if (command->m_vao.isNull()) { + qCDebug(Rendering) << Q_FUNC_INFO << "Allocating new VAO"; + command->m_vao = vaoManager->getOrAcquireHandle(vaoKey); + vaoManager->data(command->m_vao)->create(m_submissionContext.data(), vaoKey); + } + + if (*previousVaoHandle != command->m_vao) { + *previousVaoHandle = command->m_vao; + *vao = vaoManager->data(command->m_vao); + } + Q_ASSERT(*vao); +} + +// Called by RenderView->submit() in RenderThread context +// Returns true, if all RenderCommands were sent to the GPU +bool Renderer::executeCommandsSubmission(const RenderView *rv) +{ + bool allCommandsIssued = true; + + // Render drawing commands + QVector<RenderCommand> commands = rv->commands(); + + // Use the graphicscontext to submit the commands to the underlying + // graphics API (OpenGL) + + // Save the RenderView base stateset + RenderStateSet *globalState = m_submissionContext->currentStateSet(); + OpenGLVertexArrayObject *vao = nullptr; + + for (RenderCommand &command : commands) { + + if (command.m_type == RenderCommand::Compute) { // Compute Call + performCompute(rv, &command); + } else { // Draw Command + // Check if we have a valid command that can be drawn + if (!command.m_isValid) { + allCommandsIssued = false; + continue; + } + + vao = m_glResourceManagers->vaoManager()->data(command.m_vao); + + // something may have went wrong when initializing the VAO + if (!vao->isSpecified()) { + allCommandsIssued = false; + continue; + } + + { + Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); + //// We activate the shader here + GLShader *shader = command.m_glShader; + if (!m_submissionContext->activateShader(shader)) { + allCommandsIssued = false; + continue; + } + } + + { + Profiling::GLTimeRecorder recorder(Profiling::VAOUpdate); + // Bind VAO + vao->bind(); + } + + { + Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); + //// Update program uniforms + if (!m_submissionContext->setParameters(command.m_parameterPack)) { + allCommandsIssued = false; + // If we have failed to set uniform (e.g unable to bind a texture) + // we won't perform the draw call which could show invalid content + continue; + } + } + + //// OpenGL State + // TO DO: Make states not dependendent on their backend node for this step + // Set state + RenderStateSet *localState = command.m_stateSet.data(); + + + { + Profiling::GLTimeRecorder recorder(Profiling::StateUpdate); + // Merge the RenderCommand state with the globalState of the RenderView + // Or restore the globalState if no stateSet for the RenderCommand + if (localState != nullptr) { + command.m_stateSet->merge(globalState); + m_submissionContext->setCurrentStateSet(localState); + } else { + m_submissionContext->setCurrentStateSet(globalState); + } + } + // All Uniforms for a pass are stored in the QUniformPack of the command + // Uniforms for Effect, Material and Technique should already have been correctly resolved + // at that point + + //// Draw Calls + performDraw(&command); + } + } // end of RenderCommands loop + + // We cache the VAO and release it only at the end of the exectute frame + // We try to minimize VAO binding between RenderCommands + if (vao) + vao->release(); + + // Reset to the state we were in before executing the render commands + m_submissionContext->setCurrentStateSet(globalState); + + return allCommandsIssued; +} + +bool Renderer::updateVAOWithAttributes(Geometry *geometry, + const RenderCommand *command, + GLShader *shader, + bool forceUpdate) +{ + m_dirtyAttributes.reserve(m_dirtyAttributes.size() + geometry->attributes().size()); + const auto attributeIds = geometry->attributes(); + + for (QNodeId attributeId : attributeIds) { + // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager + Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + + if (attribute == nullptr) + return false; + + Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attribute->bufferId()); + + // Buffer update was already performed at this point + // Just make sure the attribute reference a valid buffer + if (buffer == nullptr) + return false; + + // Index Attribute + bool attributeWasDirty = false; + if (attribute->attributeType() == QAttribute::IndexAttribute) { + if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) + m_submissionContext->specifyIndices(buffer); + // Vertex Attribute + } else if (command->m_activeAttributes.contains(attribute->nameId())) { + if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) { + // Find the location for the attribute + const QVector<ShaderAttribute> shaderAttributes = shader->attributes(); + const ShaderAttribute *attributeDescription = nullptr; + for (const ShaderAttribute &shaderAttribute : shaderAttributes) { + if (shaderAttribute.m_nameId == attribute->nameId()) { + attributeDescription = &shaderAttribute; + break; + } + } + if (!attributeDescription || attributeDescription->m_location < 0) + return false; + m_submissionContext->specifyAttribute(attribute, buffer, attributeDescription); + } + } + + // Append attribute to temporary vector so that its dirtiness + // can be cleared at the end of the frame + if (attributeWasDirty) + m_dirtyAttributes.push_back(attribute); + + // Note: We cannot call unsertDirty on the Attribute at this + // point as we don't know if the attributes are being shared + // with other geometry / geometryRenderer in which case they still + // should remain dirty so that VAO for these commands are properly + // updated + } + + return true; +} + +bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry, + const RenderCommand *command) const +{ + const auto attributeIds = geometry->attributes(); + + for (QNodeId attributeId : attributeIds) { + // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager + Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + + if (attribute == nullptr) + continue; + + if ((attribute->attributeType() == QAttribute::IndexAttribute && attribute->isDirty()) || + (command->m_activeAttributes.contains(attribute->nameId()) && attribute->isDirty())) + return true; + } + return false; +} + +// Erase graphics related resources that may become unused after a frame +void Renderer::cleanGraphicsResources() +{ + // Clean buffers + const QVector<Qt3DCore::QNodeId> buffersToRelease = m_nodesManager->bufferManager()->takeBuffersToRelease(); + for (Qt3DCore::QNodeId bufferId : buffersToRelease) + m_submissionContext->releaseBuffer(bufferId); + + // When Textures are cleaned up, their id is saved so that they can be + // cleaned up in the render thread + const QVector<Qt3DCore::QNodeId> cleanedUpTextureIds = std::move(m_textureIdsToCleanup); + for (const Qt3DCore::QNodeId textureCleanedUpId: cleanedUpTextureIds) + cleanupTexture(textureCleanedUpId); + + // Delete abandoned VAOs + m_abandonedVaosMutex.lock(); + const QVector<HVao> abandonedVaos = std::move(m_abandonedVaos); + m_abandonedVaosMutex.unlock(); + for (const HVao &vaoHandle : abandonedVaos) { + // might have already been destroyed last frame, but added by the cleanup job before, so + // check if the VAO is really still existent + OpenGLVertexArrayObject *vao = m_glResourceManagers->vaoManager()->data(vaoHandle); + if (vao) { + vao->destroy(); + // We remove VAO from manager using its VAOIdentifier + m_glResourceManagers->vaoManager()->release(vaoHandle); + } + } + + // Abandon GL shaders when a Shader node is destroyed Note: We are sure + // that when this gets executed, all scene changes have been received and + // shader nodes updated + const QVector<Qt3DCore::QNodeId> cleanedUpShaderIds = m_nodesManager->shaderManager()->takeShaderIdsToCleanup(); + for (const Qt3DCore::QNodeId shaderCleanedUpId: cleanedUpShaderIds) { + cleanupShader(m_nodesManager->shaderManager()->lookupResource(shaderCleanedUpId)); + // We can really release the texture at this point + m_nodesManager->shaderManager()->releaseResource(shaderCleanedUpId); + } +} + +QList<QPair<QObject *, QMouseEvent>> Renderer::pendingPickingEvents() const +{ + return m_pickEventFilter->pendingMouseEvents(); +} + +QList<QKeyEvent> Renderer::pendingKeyEvents() const +{ + return m_pickEventFilter->pendingKeyEvents(); +} + +const GraphicsApiFilterData *Renderer::contextInfo() const +{ + return m_submissionContext->contextInfo(); +} + +SubmissionContext *Renderer::submissionContext() const +{ + return m_submissionContext.data(); +} + +// Returns a vector of jobs to be performed for dirty buffers +// 1 dirty buffer == 1 job, all job can be performed in parallel +QVector<Qt3DCore::QAspectJobPtr> Renderer::createRenderBufferJobs() const +{ + const QVector<QNodeId> dirtyBuffers = m_nodesManager->bufferManager()->takeDirtyBuffers(); + QVector<QAspectJobPtr> dirtyBuffersJobs; + dirtyBuffersJobs.reserve(dirtyBuffers.size()); + + for (const QNodeId bufId : dirtyBuffers) { + Render::HBuffer bufferHandle = m_nodesManager->lookupHandle<Render::Buffer, Render::BufferManager, Render::HBuffer>(bufId); + if (!bufferHandle.isNull()) { + // Create new buffer job + auto job = Render::LoadBufferJobPtr::create(bufferHandle); + job->setNodeManager(m_nodesManager); + dirtyBuffersJobs.push_back(job); + } + } + + return dirtyBuffersJobs; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/renderer.pri b/src/plugins/renderers/opengl/renderer/renderer.pri new file mode 100644 index 000000000..0f68aa481 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderer.pri @@ -0,0 +1,28 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/openglvertexarrayobject.cpp \ + $$PWD/rendercommand.cpp \ + $$PWD/renderer.cpp \ + $$PWD/renderqueue.cpp \ + $$PWD/renderview.cpp \ + $$PWD/renderviewbuilder.cpp \ + $$PWD/shaderparameterpack.cpp \ + $$PWD/glshader.cpp \ + $$PWD/logging.cpp \ + $$PWD/commandexecuter.cpp + +HEADERS += \ + $$PWD/openglvertexarrayobject_p.h \ + $$PWD/renderercache_p.h \ + $$PWD/rendercommand_p.h \ + $$PWD/renderer_p.h \ + $$PWD/renderqueue_p.h \ + $$PWD/renderview_p.h \ + $$PWD/renderviewbuilder_p.h \ + $$PWD/shaderparameterpack_p.h \ + $$PWD/shadervariables_p.h \ + $$PWD/glshader_p.h \ + $$PWD/glfence_p.h \ + $$PWD/logging_p.h \ + $$PWD/commandexecuter_p.h diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h new file mode 100644 index 000000000..eac622192 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderer_p.h @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERER_H +#define QT3DRENDER_RENDER_RENDERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DRender/qtechnique.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/abstractrenderer_p.h> +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/qt3drender_global_p.h> +#include <Qt3DRender/private/pickboundingvolumejob_p.h> +#include <Qt3DRender/private/raycastingjob_p.h> +#include <Qt3DRender/private/rendersettings_p.h> +#include <Qt3DRender/private/expandboundingvolumejob_p.h> +#include <Qt3DRender/private/updateworldtransformjob_p.h> +#include <Qt3DRender/private/calcboundingvolumejob_p.h> +#include <Qt3DRender/private/updateshaderdatatransformjob_p.h> +#include <Qt3DRender/private/framecleanupjob_p.h> +#include <Qt3DRender/private/updateworldboundingvolumejob_p.h> +#include <Qt3DRender/private/updatetreeenabledjob_p.h> +#include <Qt3DRender/private/platformsurfacefilter_p.h> +#include <Qt3DRender/private/sendbuffercapturejob_p.h> +#include <Qt3DRender/private/genericlambdajob_p.h> +#include <Qt3DRender/private/updatemeshtrianglelistjob_p.h> +#include <Qt3DRender/private/updateskinningpalettejob_p.h> +#include <Qt3DRender/private/updateentitylayersjob_p.h> +#include <Qt3DRender/private/shaderbuilder_p.h> +#include <Qt3DRender/private/lightgatherer_p.h> +#include <Qt3DRender/private/texture_p.h> +#include <shaderparameterpack_p.h> +#include <renderviewinitializerjob_p.h> +#include <filtercompatibletechniquejob_p.h> +#include <renderercache_p.h> +#include <logging_p.h> +#include <gl_handle_types_p.h> +#include <glfence_p.h> +#include <renderercache_p.h> + +#include <QHash> +#include <QMatrix4x4> +#include <QObject> + +#include <QOpenGLShaderProgram> +#include <QOpenGLVertexArrayObject> +#include <QOpenGLBuffer> +#include <QMutex> +#include <QWaitCondition> +#include <QAtomicInt> +#include <QScopedPointer> +#include <QSemaphore> + +#include <functional> + +#if defined(QT_BUILD_INTERNAL) +class tst_Renderer; +#endif + +QT_BEGIN_NAMESPACE + +class QSurface; +class QMouseEvent; +class QScreen; + +namespace Qt3DCore { +class QEntity; +class QFrameAllocator; +class QEventFilterService; +} + +namespace Qt3DRender { + +class QCamera; +class QMaterial; +class QShaderProgram; +class QMesh; +class QRenderPass; +class QAbstractShapeMesh; +struct GraphicsApiFilterData; +class QSceneImporter; + +namespace Debug { +class CommandExecuter; +} + +namespace Render { + +class CameraLens; +class SubmissionContext; +class FrameGraphNode; +class Material; +class Technique; +class Shader; +class Entity; +class RenderCommand; +class RenderQueue; +class RenderView; +class Effect; +class RenderPass; +class RenderThread; +class RenderStateSet; +class VSyncFrameAdvanceService; +class PickEventFilter; +class NodeManagers; +class GLResourceManagers; +class GLShader; +class ResourceAccessor; + +class UpdateLevelOfDetailJob; +typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr; + +using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; +using SynchronizerPostFramePtr = GenericLambdaJobAndPostFramePtr<std::function<void ()>, std::function<void (Qt3DCore::QAspectManager *)>>; + +template<typename T, typename ... Ts> +class FilterEntityByComponentJob; +template<typename T, typename ... Ts> +using FilterEntityByComponentJobPtr = QSharedPointer<FilterEntityByComponentJob<T, Ts...>>; +using ComputableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::ComputeCommand, Render::Material>; +using RenderableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::GeometryRenderer, Render::Material>; + +class Q_3DRENDERSHARED_PRIVATE_EXPORT Renderer : public AbstractRenderer +{ +public: + explicit Renderer(QRenderAspect::RenderType type); + ~Renderer(); + + void dumpInfo() const override; + API api() const override { return AbstractRenderer::OpenGL; } + + qint64 time() const override; + void setTime(qint64 time) override; + + void setNodeManagers(NodeManagers *managers) override; + void setServices(Qt3DCore::QServiceLocator *services) override; + void setSurfaceExposed(bool exposed) override; + + NodeManagers *nodeManagers() const override; + Qt3DCore::QServiceLocator *services() const override { return m_services; } + + void initialize() override; + void shutdown() override; + void releaseGraphicsResources() override; + + void render() override; + void doRender(bool swapBuffers = true) override; + void cleanGraphicsResources() override; + + bool isRunning() const override { return m_running.loadRelaxed(); } + + void setSceneRoot(Entity *sgRoot) override; + Entity *sceneRoot() const override { return m_renderSceneRoot; } + + FrameGraphNode *frameGraphRoot() const override; + RenderQueue *renderQueue() const { return m_renderQueue; } + + void markDirty(BackendNodeDirtySet changes, BackendNode *node) override; + BackendNodeDirtySet dirtyBits() override; + +#if defined(QT_BUILD_INTERNAL) + void clearDirtyBits(BackendNodeDirtySet changes) override; +#endif + bool shouldRender() const override; + void skipNextFrame() override; + void jobsDone(Qt3DCore::QAspectManager *manager) override; + + QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() override; + QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override; + Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override; + Qt3DCore::QAspectJobPtr rayCastingJob() override; + Qt3DCore::QAspectJobPtr syncLoadingJobs() override; + Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() override; + + QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const; + + inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; } + inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; } + inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; } + inline UpdateTreeEnabledJobPtr updateTreeEnabledJob() const { return m_updateTreeEnabledJob; } + inline UpdateWorldTransformJobPtr updateWorldTransformJob() const { return m_worldTransformJob; } + inline UpdateWorldBoundingVolumeJobPtr updateWorldBoundingVolumeJob() const { return m_updateWorldBoundingVolumeJob; } + inline UpdateLevelOfDetailJobPtr updateLevelOfDetailJob() const { return m_updateLevelOfDetailJob; } + inline UpdateMeshTriangleListJobPtr updateMeshTriangleListJob() const { return m_updateMeshTriangleListJob; } + inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const { return m_filterCompatibleTechniqueJob; } + inline SynchronizerJobPtr syncLoadingJobs() const { return m_syncLoadingJobs; } + inline UpdateSkinningPaletteJobPtr updateSkinningPaletteJob() const { return m_updateSkinningPaletteJob; } + inline SynchronizerPostFramePtr introspectShadersJob() const { return m_introspectShaderJob; } + inline Qt3DCore::QAspectJobPtr bufferGathererJob() const { return m_bufferGathererJob; } + inline Qt3DCore::QAspectJobPtr textureGathererJob() const { return m_textureGathererJob; } + inline UpdateEntityLayersJobPtr updateEntityLayersJob() const { return m_updateEntityLayersJob; } + inline LightGathererPtr lightGathererJob() const { return m_lightGathererJob; } + inline RenderableEntityFilterPtr renderableEntityFilterJob() const { return m_renderableEntityFilterJob; } + inline ComputableEntityFilterPtr computableEntityFilterJob() const { return m_computableEntityFilterJob; } + inline SynchronizerJobPtr cacheLightJob() const { return m_cacheLightsJob; } + inline SynchronizerJobPtr cacheRenderableEntitiesJob() const { return m_cacheRenderableEntitiesJob; } + inline SynchronizerJobPtr cacheComputableEntitiesJob() const { return m_cacheComputableEntitiesJob; } + + Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override; + + void registerEventFilter(Qt3DCore::QEventFilterService *service) override; + + void setSettings(RenderSettings *settings) override; + RenderSettings *settings() const override; + QOpenGLContext *shareContext() const override; + + inline GLResourceManagers *glResourceManagers() const { return m_glResourceManagers; } + + // Executed in secondary GL thread + void loadShader(Shader *shader, Qt3DRender::Render::HShader shaderHandle) override; + + + void updateGLResources(); + void updateTexture(Texture *texture); + void cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId); + void cleanupShader(const Shader *shader); + void downloadGLBuffers(); + void blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, + Qt3DCore::QNodeId outputRenderTargetId, + QRect inputRect, + QRect outputRect, + GLuint defaultFramebuffer); + + void prepareCommandsSubmission(const QVector<RenderView *> &renderViews); + bool executeCommandsSubmission(const RenderView *rv); + bool updateVAOWithAttributes(Geometry *geometry, + const RenderCommand *command, + GLShader *shader, + bool forceUpdate); + + bool requiresVAOAttributeUpdate(Geometry *geometry, + const RenderCommand *command) const; + + // For Scene2D rendering + void setOpenGLContext(QOpenGLContext *context) override; + bool accessOpenGLTexture(Qt3DCore::QNodeId nodeId, + QOpenGLTexture **texture, + QMutex **lock, + bool readonly) override; + QSharedPointer<RenderBackendResourceAccessor> resourceAccessor() const override; + + + const GraphicsApiFilterData *contextInfo() const; + SubmissionContext *submissionContext() const; + + inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; } + + QList<QPair<QObject*, QMouseEvent>> pendingPickingEvents() const; + QList<QKeyEvent> pendingKeyEvents() const; + + void enqueueRenderView(RenderView *renderView, int submitOrder); + bool isReadyToSubmit(); + + QVariant executeCommand(const QStringList &args) override; + void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override; + QSurfaceFormat format() override; + + struct ViewSubmissionResultData + { + ViewSubmissionResultData() + : lastBoundFBOId(0) + , surface(nullptr) + {} + + uint lastBoundFBOId; + QSurface *surface; + }; + + ViewSubmissionResultData submitRenderViews(const QVector<Render::RenderView *> &renderViews); + + RendererCache *cache() { return &m_cache; } + void setScreen(QScreen *scr) override; + QScreen *screen() const override; + +#ifdef QT3D_RENDER_UNIT_TESTS +public: +#else + +private: +#endif + bool canRender() const; + + Qt3DCore::QServiceLocator *m_services; + NodeManagers *m_nodesManager; + + // Frame graph root + Qt3DCore::QNodeId m_frameGraphRootUuid; + + Entity *m_renderSceneRoot; + + // Fail safe values that we can use if a RenderCommand + // is missing a shader + RenderStateSet *m_defaultRenderStateSet; + ShaderParameterPack m_defaultUniformPack; + + QScopedPointer<SubmissionContext> m_submissionContext; + QSurfaceFormat m_format; + + RenderQueue *m_renderQueue; + QScopedPointer<RenderThread> m_renderThread; + QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService; + + QSemaphore m_submitRenderViewsSemaphore; + QSemaphore m_waitForInitializationToBeCompleted; + QMutex m_hasBeenInitializedMutex; + + QAtomicInt m_running; + + QScopedPointer<PickEventFilter> m_pickEventFilter; + + QVector<Attribute *> m_dirtyAttributes; + QVector<Geometry *> m_dirtyGeometry; + QAtomicInt m_exposed; + + struct DirtyBits { + BackendNodeDirtySet marked; // marked dirty since last job build + BackendNodeDirtySet remaining; // remaining dirty after jobs have finished + }; + DirtyBits m_dirtyBits; + + QAtomicInt m_lastFrameCorrect; + QOpenGLContext *m_glContext; + QOpenGLContext *m_shareContext; + mutable QMutex m_shareContextMutex; + PickBoundingVolumeJobPtr m_pickBoundingVolumeJob; + RayCastingJobPtr m_rayCastingJob; + + qint64 m_time; + + RenderSettings *m_settings; + + UpdateShaderDataTransformJobPtr m_updateShaderDataTransformJob; + FrameCleanupJobPtr m_cleanupJob; + UpdateWorldTransformJobPtr m_worldTransformJob; + ExpandBoundingVolumeJobPtr m_expandBoundingVolumeJob; + CalculateBoundingVolumeJobPtr m_calculateBoundingVolumeJob; + UpdateWorldBoundingVolumeJobPtr m_updateWorldBoundingVolumeJob; + UpdateTreeEnabledJobPtr m_updateTreeEnabledJob; + SendBufferCaptureJobPtr m_sendBufferCaptureJob; + UpdateSkinningPaletteJobPtr m_updateSkinningPaletteJob; + UpdateLevelOfDetailJobPtr m_updateLevelOfDetailJob; + UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob; + FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob; + UpdateEntityLayersJobPtr m_updateEntityLayersJob; + LightGathererPtr m_lightGathererJob; + RenderableEntityFilterPtr m_renderableEntityFilterJob; + ComputableEntityFilterPtr m_computableEntityFilterJob; + + QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests; + + void performDraw(RenderCommand *command); + void performCompute(const RenderView *rv, RenderCommand *command); + void createOrUpdateVAO(RenderCommand *command, + HVao *previousVAOHandle, + OpenGLVertexArrayObject **vao); + + SynchronizerJobPtr m_bufferGathererJob; + SynchronizerJobPtr m_vaoGathererJob; + SynchronizerJobPtr m_textureGathererJob; + SynchronizerJobPtr m_sendSetFenceHandlesToFrontendJob; + SynchronizerPostFramePtr m_introspectShaderJob; + SynchronizerJobPtr m_syncLoadingJobs; + SynchronizerJobPtr m_cacheRenderableEntitiesJob; + SynchronizerJobPtr m_cacheComputableEntitiesJob; + SynchronizerJobPtr m_cacheLightsJob; + + void lookForAbandonedVaos(); + void lookForDirtyBuffers(); + void lookForDownloadableBuffers(); + void lookForDirtyTextures(); + void reloadDirtyShaders(); + void sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager); + void sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager); + void sendSetFenceHandlesToFrontend(); + void sendDisablesToFrontend(Qt3DCore::QAspectManager *manager); + + QMutex m_abandonedVaosMutex; + QVector<HVao> m_abandonedVaos; + + QVector<HBuffer> m_dirtyBuffers; + QVector<Qt3DCore::QNodeId> m_downloadableBuffers; + QVector<HShader> m_dirtyShaders; + QVector<HTexture> m_dirtyTextures; + QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> m_updatedTextureProperties; + QVector<QPair<Qt3DCore::QNodeId, GLFence>> m_updatedSetFences; + QVector<Qt3DCore::QNodeId> m_updatedDisableSubtreeEnablers; + Qt3DCore::QNodeIdVector m_textureIdsToCleanup; + QVector<ShaderBuilderUpdate> m_shaderBuilderUpdates; + + bool m_ownedContext; + + OffscreenSurfaceHelper *m_offscreenHelper; + GLResourceManagers *m_glResourceManagers; + QMutex m_offscreenSurfaceMutex; + + QScopedPointer<Qt3DRender::Debug::CommandExecuter> m_commandExecuter; + +#ifdef QT_BUILD_INTERNAL + friend class ::tst_Renderer; +#endif + + QMetaObject::Connection m_contextConnection; + RendererCache m_cache; + bool m_shouldSwapBuffers; + + QVector<FrameGraphNode *> m_frameGraphLeaves; + QScreen *m_screen = nullptr; + QSharedPointer<ResourceAccessor> m_scene2DResourceAccessor; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERER_H diff --git a/src/plugins/renderers/opengl/renderer/renderercache_p.h b/src/plugins/renderers/opengl/renderer/renderercache_p.h new file mode 100644 index 000000000..70a5ef43c --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderercache_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERERCACHE_P_H +#define QT3DRENDER_RENDER_RENDERERCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/QFrameGraphNode> + +#include <Qt3DRender/private/entity_p.h> +#include <renderviewjobutils_p.h> +#include <Qt3DRender/private/lightsource_p.h> +#include <rendercommand_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +struct RendererCache +{ + struct LeafNodeData + { + QVector<Entity *> filterEntitiesByLayer; + MaterialParameterGathererData materialParameterGatherer; + EntityRenderCommandData renderCommandData; + }; + + // Shared amongst all RV cache + QVector<Entity *> renderableEntities; + QVector<Entity *> computeEntities; + QVector<LightSource> gatheredLights; + EnvironmentLight* environmentLight; + + // Per RV cache + QHash<FrameGraphNode *, LeafNodeData> leafNodeCache; + + QMutex *mutex() { return &m_mutex; } + +private: + QMutex m_mutex; +}; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERERCACHE_P_H diff --git a/src/plugins/renderers/opengl/renderer/renderqueue.cpp b/src/plugins/renderers/opengl/renderer/renderqueue.cpp new file mode 100644 index 000000000..9ba66952f --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderqueue.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderqueue_p.h" +#include <renderview_p.h> +#include <QThread> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +RenderQueue::RenderQueue() + : m_noRender(false) + , m_wasReset(true) + , m_targetRenderViewCount(0) + , m_currentRenderViewCount(0) + , m_currentWorkQueue(1) +{ +} + +int RenderQueue::currentRenderViewCount() const +{ + return m_currentRenderViewCount; +} + +/* + * In case the framegraph changed or when the current number of render queue + * needs to be reset. + */ +void RenderQueue::reset() +{ + m_currentRenderViewCount = 0; + m_targetRenderViewCount = 0; + m_currentWorkQueue.clear(); + m_noRender = false; + m_wasReset = true; +} + +void RenderQueue::setNoRender() +{ + Q_ASSERT(m_targetRenderViewCount == 0); + m_noRender = true; +} + +/* + * Queue up a RenderView for the frame being built. + * Thread safe as this is called from the renderer which is locked. + * Returns true if the renderView is complete + */ +bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIndex) +{ + Q_ASSERT(!m_noRender); + m_currentWorkQueue[submissionOrderIndex] = renderView; + ++m_currentRenderViewCount; + Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount); + return isFrameQueueComplete(); +} + +/* + * Called by the Rendering Thread to retrieve the a frame queue to render. + * A call to reset is required after rendering of the frame. Otherwise under some + * conditions the current but then invalidated frame queue could be reused. + */ +QVector<RenderView *> RenderQueue::nextFrameQueue() +{ + return m_currentWorkQueue; +} + +/* + * Sets the number \a targetRenderViewCount of RenderView objects that make up a frame. + */ +void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount) +{ + Q_ASSERT(!m_noRender); + m_targetRenderViewCount = targetRenderViewCount; + m_currentWorkQueue.resize(targetRenderViewCount); + m_wasReset = false; +} + +/* + * Returns true if all the RenderView objects making up the current frame have been queued. + * Returns false otherwise. + * \note a frameQueue or size 0 is considered incomplete. + */ +bool RenderQueue::isFrameQueueComplete() const +{ + return (m_noRender + || (m_targetRenderViewCount > 0 && m_targetRenderViewCount == m_currentRenderViewCount)); +} + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/renderqueue_p.h b/src/plugins/renderers/opengl/renderer/renderqueue_p.h new file mode 100644 index 000000000..e565115f2 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderqueue_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERQUEUE_H +#define QT3DRENDER_RENDER_RENDERQUEUE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QVector> +#include <QtGlobal> +#include <QMutex> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class RenderView; + +class Q_AUTOTEST_EXPORT RenderQueue +{ +public: + RenderQueue(); + + void setTargetRenderViewCount(int targetRenderViewCount); + int targetRenderViewCount() const { return m_targetRenderViewCount; } + int currentRenderViewCount() const; + bool isFrameQueueComplete() const; + + bool queueRenderView(RenderView *renderView, uint submissionOrderIndex); + QVector<RenderView *> nextFrameQueue(); + void reset(); + + void setNoRender(); + inline bool isNoRender() const { return m_noRender; } + + inline bool wasReset() const { return m_wasReset; } + + inline QMutex *mutex() { return &m_mutex; } + +private: + bool m_noRender; + bool m_wasReset; + int m_targetRenderViewCount; + int m_currentRenderViewCount; + QVector<RenderView *> m_currentWorkQueue; + QMutex m_mutex; +}; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERQUEUE_H diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp new file mode 100644 index 000000000..bd4c98a01 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderview.cpp @@ -0,0 +1,1229 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderview_p.h" +#include <Qt3DRender/qmaterial.h> +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DRender/qrendertarget.h> +#include <Qt3DRender/qabstractlight.h> +#include <Qt3DRender/private/sphere_p.h> + +#include <Qt3DRender/private/cameraselectornode_p.h> +#include <Qt3DRender/private/framegraphnode_p.h> +#include <Qt3DRender/private/layerfilternode_p.h> +#include <Qt3DRender/private/qparameter_p.h> +#include <Qt3DRender/private/cameralens_p.h> +#include <Qt3DRender/private/effect_p.h> +#include <Qt3DRender/private/entity_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/layer_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/renderpassfilternode_p.h> +#include <Qt3DRender/private/renderpass_p.h> +#include <Qt3DRender/private/geometryrenderer_p.h> +#include <Qt3DRender/private/techniquefilternode_p.h> +#include <Qt3DRender/private/viewportnode_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/geometryrenderermanager_p.h> +#include <Qt3DRender/private/rendercapture_p.h> +#include <Qt3DRender/private/buffercapture_p.h> +#include <Qt3DRender/private/stringtoint_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <rendercommand_p.h> +#include <renderer_p.h> +#include <graphicscontext_p.h> +#include <submissioncontext_p.h> +#include <glresourcemanagers_p.h> +#include <Qt3DCore/qentity.h> +#include <QtGui/qsurface.h> +#include <algorithm> + +#include <QDebug> +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) +#include <QElapsedTimer> +#endif + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + + +namespace { + +// register our QNodeId's as a metatype during program loading +const int Q_DECL_UNUSED qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>(); + +const int MAX_LIGHTS = 8; + +#define LIGHT_POSITION_NAME QLatin1String(".position") +#define LIGHT_TYPE_NAME QLatin1String(".type") +#define LIGHT_COLOR_NAME QLatin1String(".color") +#define LIGHT_INTENSITY_NAME QLatin1String(".intensity") + +int LIGHT_COUNT_NAME_ID = 0; +int LIGHT_POSITION_NAMES[MAX_LIGHTS]; +int LIGHT_TYPE_NAMES[MAX_LIGHTS]; +int LIGHT_COLOR_NAMES[MAX_LIGHTS]; +int LIGHT_INTENSITY_NAMES[MAX_LIGHTS]; +QString LIGHT_STRUCT_NAMES[MAX_LIGHTS]; + +int LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS]; +int LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS]; +int LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS]; +int LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS]; +QString LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS]; + +bool wasInitialized = false; + +} // anonymous namespace + +RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters; + + +RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniformSetters() +{ + RenderView::StandardUniformsNameToTypeHash setters; + + setters.insert(Shader::modelMatrixNameId, ModelMatrix); + setters.insert(Shader::viewMatrixNameId, ViewMatrix); + setters.insert(Shader::projectionMatrixNameId, ProjectionMatrix); + setters.insert(Shader::modelViewMatrixNameId, ModelViewMatrix); + setters.insert(Shader::viewProjectionMatrixNameId, ViewProjectionMatrix); + setters.insert(Shader::modelViewProjectionNameId, ModelViewProjectionMatrix); + setters.insert(Shader::mvpNameId, ModelViewProjectionMatrix); + setters.insert(Shader::inverseModelMatrixNameId, InverseModelMatrix); + setters.insert(Shader::inverseViewMatrixNameId, InverseViewMatrix); + setters.insert(Shader::inverseProjectionMatrixNameId, InverseProjectionMatrix); + setters.insert(Shader::inverseModelViewNameId, InverseModelViewMatrix); + setters.insert(Shader::inverseViewProjectionMatrixNameId, InverseViewProjectionMatrix); + setters.insert(Shader::inverseModelViewProjectionNameId, InverseModelViewProjectionMatrix); + setters.insert(Shader::modelNormalMatrixNameId, ModelNormalMatrix); + setters.insert(Shader::modelViewNormalNameId, ModelViewNormalMatrix); + setters.insert(Shader::viewportMatrixNameId, ViewportMatrix); + setters.insert(Shader::inverseViewportMatrixNameId, InverseViewportMatrix); + setters.insert(Shader::aspectRatioNameId, AspectRatio); + setters.insert(Shader::exposureNameId, Exposure); + setters.insert(Shader::gammaNameId, Gamma); + setters.insert(Shader::timeNameId, Time); + setters.insert(Shader::eyePositionNameId, EyePosition); + setters.insert(Shader::skinningPaletteNameId, SkinningPalette); + + return setters; +} + +// TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too +static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize) +{ + return QRectF(fractionalViewport.x() * surfaceSize.width(), + (1.0 - fractionalViewport.y() - fractionalViewport.height()) * surfaceSize.height(), + fractionalViewport.width() * surfaceSize.width(), + fractionalViewport.height() * surfaceSize.height()); +} + +static Matrix4x4 getProjectionMatrix(const CameraLens *lens) +{ + if (!lens) + qWarning() << "[Qt3D Renderer] No Camera Lens found. Add a CameraSelector to your Frame Graph or make sure that no entities will be rendered."; + return lens ? lens->projection() : Matrix4x4(); +} + +UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType, + Entity *entity, + const Matrix4x4 &model) const +{ + switch (standardUniformType) { + case ModelMatrix: + return UniformValue(model); + case ViewMatrix: + return UniformValue(m_data.m_viewMatrix); + case ProjectionMatrix: + return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens)); + case ModelViewMatrix: + return UniformValue(m_data.m_viewMatrix * model); + case ViewProjectionMatrix: + return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix); + case ModelViewProjectionMatrix: + return UniformValue(m_data.m_viewProjectionMatrix * model); + case InverseModelMatrix: + return UniformValue(model.inverted()); + case InverseViewMatrix: + return UniformValue(m_data.m_viewMatrix.inverted()); + case InverseProjectionMatrix: { + return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens).inverted()); + } + case InverseModelViewMatrix: + return UniformValue((m_data.m_viewMatrix * model).inverted()); + case InverseViewProjectionMatrix: { + const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix; + return UniformValue(viewProjectionMatrix.inverted()); + } + case InverseModelViewProjectionMatrix: + return UniformValue((m_data.m_viewProjectionMatrix * model).inverted()); + case ModelNormalMatrix: + return UniformValue(convertToQMatrix4x4(model).normalMatrix()); + case ModelViewNormalMatrix: + return UniformValue(convertToQMatrix4x4(m_data.m_viewMatrix * model).normalMatrix()); + case ViewportMatrix: { + QMatrix4x4 viewportMatrix; + // TO DO: Implement on Matrix4x4 + viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); + return UniformValue(Matrix4x4(viewportMatrix)); + } + case InverseViewportMatrix: { + QMatrix4x4 viewportMatrix; + // TO DO: Implement on Matrix4x4 + viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); + return UniformValue(Matrix4x4(viewportMatrix.inverted())); + } + case AspectRatio: + return float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height())); + case Exposure: + return UniformValue(m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f); + case Gamma: + return UniformValue(m_gamma); + case Time: + return UniformValue(float(m_renderer->time() / 1000000000.0f)); + case EyePosition: + return UniformValue(m_data.m_eyePos); + case SkinningPalette: { + const Armature *armature = entity->renderComponent<Armature>(); + if (!armature) { + qCWarning(Jobs, "Requesting skinningPalette uniform but no armature set on entity"); + return UniformValue(); + } + return armature->skinningPaletteUniform(); + } + default: + Q_UNREACHABLE(); + return UniformValue(); + } +} + +RenderView::RenderView() + : m_isDownloadBuffersEnable(false) + , m_hasBlitFramebufferInfo(false) + , m_renderer(nullptr) + , m_manager(nullptr) + , m_devicePixelRatio(1.) + , m_viewport(QRectF(0., 0., 1., 1.)) + , m_gamma(2.2f) + , m_surface(nullptr) + , m_clearBuffer(QClearBuffers::None) + , m_clearDepthValue(1.f) + , m_clearStencilValue(0) + , m_stateSet(nullptr) + , m_noDraw(false) + , m_compute(false) + , m_frustumCulling(false) + , m_memoryBarrier(QMemoryBarrier::None) + , m_environmentLight(nullptr) +{ + m_workGroups[0] = 1; + m_workGroups[1] = 1; + m_workGroups[2] = 1; + + if (Q_UNLIKELY(!wasInitialized)) { + // Needed as we can control the init order of static/global variables across compile units + // and this hash relies on the static StringToInt class + wasInitialized = true; + RenderView::ms_standardUniformSetters = RenderView::initializeStandardUniformSetters(); + LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount")); + for (int i = 0; i < MAX_LIGHTS; ++i) { + Q_STATIC_ASSERT_X(MAX_LIGHTS < 10, "can't use the QChar trick anymore"); + LIGHT_STRUCT_NAMES[i] = QLatin1String("lights[") + QLatin1Char(char('0' + i)) + QLatin1Char(']'); + LIGHT_POSITION_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_POSITION_NAME); + LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME); + LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME); + LIGHT_INTENSITY_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME); + + LIGHT_STRUCT_UNROLL_NAMES[i] = QLatin1String("light_") + QLatin1Char(char('0' + i)); + LIGHT_POSITION_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_POSITION_NAME); + LIGHT_TYPE_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_TYPE_NAME); + LIGHT_COLOR_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_COLOR_NAME); + LIGHT_INTENSITY_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_INTENSITY_NAME); + } + } +} + +RenderView::~RenderView() +{ + delete m_stateSet; +} + +namespace { + +template<int SortType> +struct AdjacentSubRangeFinder +{ + static bool adjacentSubRange(const RenderCommand &, const RenderCommand &) + { + Q_UNREACHABLE(); + return false; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost> +{ + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) + { + return a.m_changeCost == b.m_changeCost; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::BackToFront> +{ + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) + { + return a.m_depth == b.m_depth; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::Material> +{ + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) + { + return a.m_glShader == b.m_glShader; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack> +{ + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) + { + return a.m_depth == b.m_depth; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::Texture> +{ + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) + { + // Two renderCommands are adjacent if one contains all the other command's textures + QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); + + if (texturesB.size() > texturesA.size()) + qSwap(texturesA, texturesB); + + // textureB.size() is always <= textureA.size() + for (const ShaderParameterPack::NamedResource &texB : qAsConst(texturesB)) { + if (!texturesA.contains(texB)) + return false; + } + return true; + } +}; + +template<typename Predicate> +int advanceUntilNonAdjacent(const QVector<RenderCommand> &commands, + const int beg, const int end, Predicate pred) +{ + int i = beg + 1; + while (i < end) { + if (!pred(*(commands.begin() + beg), *(commands.begin() + i))) + break; + ++i; + } + return i; +} + + +using CommandIt = QVector<RenderCommand>::iterator; + +template<int SortType> +struct SubRangeSorter +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + Q_UNUSED(begin); + Q_UNUSED(end); + Q_UNREACHABLE(); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::StateChangeCost> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_changeCost > b.m_changeCost; + }); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::BackToFront> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_depth > b.m_depth; + }); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::Material> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + // First we sort by shader + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_glShader > b.m_glShader; + }); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::FrontToBack> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_depth < b.m_depth; + }); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::Texture> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); + + const int originalTextureASize = texturesA.size(); + + if (texturesB.size() > texturesA.size()) + qSwap(texturesA, texturesB); + + int identicalTextureCount = 0; + + for (const ShaderParameterPack::NamedResource &texB : qAsConst(texturesB)) { + if (texturesA.contains(texB)) + ++identicalTextureCount; + } + + return identicalTextureCount < originalTextureASize; + }); + } +}; + +int findSubRange(const QVector<RenderCommand> &commands, + const int begin, const int end, + const QSortPolicy::SortType sortType) +{ + switch (sortType) { + case QSortPolicy::StateChangeCost: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange); + case QSortPolicy::BackToFront: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange); + case QSortPolicy::Material: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + case QSortPolicy::FrontToBack: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange); + case QSortPolicy::Texture: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange); + default: + Q_UNREACHABLE(); + return end; + } +} + +void sortByMaterial(QVector<RenderCommand> &commands, int begin, const int end) +{ + // We try to arrange elements so that their rendering cost is minimized for a given shader + int rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + while (begin != end) { + if (begin + 1 < rangeEnd) { + std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (const RenderCommand &a, const RenderCommand &b){ + return a.m_material.handle() < b.m_material.handle(); + }); + } + begin = rangeEnd; + rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + } +} + +void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end, const int level, + const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes) +{ + if (level >= sortingTypes.size()) + return; + + switch (sortingTypes.at(level)) { + case QSortPolicy::StateChangeCost: + SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(commands.begin() + begin, commands.begin() + end); + break; + case QSortPolicy::BackToFront: + SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(commands.begin() + begin, commands.begin() + end); + break; + case QSortPolicy::Material: + // Groups all same shader DNA together + SubRangeSorter<QSortPolicy::Material>::sortSubRange(commands.begin() + begin, commands.begin() + end); + // Group all same material together (same parameters most likely) + sortByMaterial(commands, begin, end); + break; + case QSortPolicy::FrontToBack: + SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(commands.begin() + begin, commands.begin() + end); + break; + case QSortPolicy::Texture: + SubRangeSorter<QSortPolicy::Texture>::sortSubRange(commands.begin() + begin, commands.begin() + end); + break; + default: + Q_UNREACHABLE(); + } + + // For all sub ranges of adjacent item for sortType[i] + // Perform filtering with sortType[i + 1] + int rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level)); + while (begin != end) { + sortCommandRange(commands, begin, rangeEnd, level + 1, sortingTypes); + begin = rangeEnd; + rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level)); + } +} + +} // anonymous + +void RenderView::sort() +{ + // Compares the bitsetKey of the RenderCommands + // Key[Depth | StateCost | Shader] + sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes); + + // For RenderCommand with the same shader + // We compute the adjacent change cost + + // Minimize uniform changes + int i = 0; + const int commandSize = m_commands.size(); + while (i < commandSize) { + int j = i; + + // Advance while commands share the same shader + while (i < commandSize && + m_commands[j].m_glShader == m_commands[i].m_glShader) + ++i; + + if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes + PackUniformHash cachedUniforms = m_commands[j++].m_parameterPack.uniforms(); + + while (j < i) { + // We need the reference here as we are modifying the original container + // not the copy + PackUniformHash &uniforms = m_commands[j].m_parameterPack.m_uniforms; + + for (int u = 0; u < uniforms.keys.size();) { + // We are comparing the values: + // - raw uniform values + // - the texture Node id if the uniform represents a texture + // since all textures are assigned texture units before the RenderCommands + // sharing the same material (shader) are rendered, we can't have the case + // where two uniforms, referencing the same texture eventually have 2 different + // texture unit values + const int uniformNameId = uniforms.keys.at(u); + const UniformValue &refValue = cachedUniforms.value(uniformNameId); + const UniformValue &newValue = uniforms.values.at(u); + if (newValue == refValue) { + uniforms.erase(u); + } else { + // Record updated value so that subsequent comparison + // for the next command will be made againts latest + // uniform value + cachedUniforms.insert(uniformNameId, newValue); + ++u; + } + } + ++j; + } + } + } +} + +void RenderView::setRenderer(Renderer *renderer) +{ + m_renderer = renderer; + m_manager = renderer->nodeManagers(); +} + +void RenderView::addClearBuffers(const ClearBuffers *cb) { + QClearBuffers::BufferTypeFlags type = cb->type(); + + if (type & QClearBuffers::StencilBuffer) { + m_clearStencilValue = cb->clearStencilValue(); + m_clearBuffer |= QClearBuffers::StencilBuffer; + } + if (type & QClearBuffers::DepthBuffer) { + m_clearDepthValue = cb->clearDepthValue(); + m_clearBuffer |= QClearBuffers::DepthBuffer; + } + // keep track of global ClearColor (if set) and collect all DrawBuffer-specific + // ClearColors + if (type & QClearBuffers::ColorBuffer) { + ClearBufferInfo clearBufferInfo; + clearBufferInfo.clearColor = cb->clearColor(); + + if (cb->clearsAllColorBuffers()) { + m_globalClearColorBuffer = clearBufferInfo; + m_clearBuffer |= QClearBuffers::ColorBuffer; + } else { + if (cb->bufferId()) { + const RenderTargetOutput *targetOutput = m_manager->attachmentManager()->lookupResource(cb->bufferId()); + if (targetOutput) { + clearBufferInfo.attchmentPoint = targetOutput->point(); + // Note: a job is later performed to find the drawIndex from the buffer attachment point + // using the AttachmentPack + m_specificClearColorBuffers.push_back(clearBufferInfo); + } + } + } + } +} + +// If we are there, we know that entity had a GeometryRenderer + Material +EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const +{ + GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager(); + EntityRenderCommandData commands; + + commands.reserve(count); + + for (int i = 0; i < count; ++i) { + const int idx = offset + i; + Entity *entity = entities.at(idx); + GeometryRenderer *geometryRenderer = nullptr; + HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>(); + + // There is a geometry renderer with geometry + if ((geometryRenderer = m_manager->geometryRendererManager()->data(geometryRendererHandle)) != nullptr + && geometryRenderer->isEnabled() + && !geometryRenderer->geometryId().isNull()) { + + const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); + const HMaterial materialHandle = entity->componentHandle<Material>(); + const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId); + + HGeometry geometryHandle = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); + Geometry *geometry = m_manager->geometryManager()->data(geometryHandle); + + // 1 RenderCommand per RenderPass pass on an Entity with a Mesh + for (const RenderPassParameterData &passData : renderPassData) { + // Add the RenderPass Parameters + RenderCommand command = {}; + command.m_geometryRenderer = geometryRendererHandle; + command.m_geometry = geometryHandle; + + command.m_material = materialHandle; + // For RenderPass based states we use the globally set RenderState + // if no renderstates are defined as part of the pass. That means: + // RenderPass { renderStates: [] } will use the states defined by + // StateSet in the FrameGraph + RenderPass *pass = passData.pass; + if (pass->hasRenderStates()) { + command.m_stateSet = RenderStateSetPtr::create(); + addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager()); + if (m_stateSet != nullptr) + command.m_stateSet->merge(m_stateSet); + command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); + } + command.m_shaderId = pass->shaderProgram(); + command.m_glShader = glShaderManager->lookupResource(command.m_shaderId); + + // It takes two frames to have a valid command as we can only + // reference a glShader at frame n if it has been loaded at frame n - 1 + if (!command.m_glShader) + continue; + + { // Scoped to show extent + + // Update the draw command with what's going to be needed for the drawing + int primitiveCount = geometryRenderer->vertexCount(); + int estimatedCount = 0; + Attribute *indexAttribute = nullptr; + Attribute *indirectAttribute = nullptr; + + const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes(); + for (Qt3DCore::QNodeId attributeId : attributeIds) { + Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId); + switch (attribute->attributeType()) { + case QAttribute::IndexAttribute: + indexAttribute = attribute; + break; + case QAttribute::DrawIndirectAttribute: + indirectAttribute = attribute; + break; + case QAttribute::VertexAttribute: + estimatedCount = std::max(int(attribute->count()), estimatedCount); + break; + default: + Q_UNREACHABLE(); + break; + } + } + + command.m_drawIndexed = (indexAttribute != nullptr); + command.m_drawIndirect = (indirectAttribute != nullptr); + + // Update the draw command with all the information required for the drawing + if (command.m_drawIndexed) { + command.m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); + command.m_indexAttributeByteOffset = indexAttribute->byteOffset() + geometryRenderer->indexBufferByteOffset(); + } + + // Note: we only care about the primitiveCount when using direct draw calls + // For indirect draw calls it is assumed the buffer was properly set already + if (command.m_drawIndirect) { + command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); + command.m_indirectDrawBuffer = m_manager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); + } else { + // Use the count specified by the GeometryRender + // If not specify use the indexAttribute count if present + // Otherwise tries to use the count from the attribute with the highest count + if (primitiveCount == 0) { + if (indexAttribute) + primitiveCount = indexAttribute->count(); + else + primitiveCount = estimatedCount; + } + } + + command.m_primitiveCount = primitiveCount; + command.m_primitiveType = geometryRenderer->primitiveType(); + command.m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled(); + command.m_restartIndexValue = geometryRenderer->restartIndexValue(); + command.m_firstInstance = geometryRenderer->firstInstance(); + command.m_instanceCount = geometryRenderer->instanceCount(); + command.m_firstVertex = geometryRenderer->firstVertex(); + command.m_indexOffset = geometryRenderer->indexOffset(); + command.m_verticesPerPatch = geometryRenderer->verticesPerPatch(); + } // scope + + + commands.push_back(entity, + std::move(command), + std::move(passData)); + } + } + } + + return commands; +} + +EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const +{ + // If the RenderView contains only a ComputeDispatch then it cares about + // A ComputeDispatch is also implicitely a NoDraw operation + // enabled flag + // layer component + // material/effect/technique/parameters/filters/ + EntityRenderCommandData commands; + GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager(); + + commands.reserve(count); + + for (int i = 0; i < count; ++i) { + const int idx = offset + i; + Entity *entity = entities.at(idx); + ComputeCommand *computeJob = nullptr; + HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>(); + if ((computeJob = nodeManagers()->computeJobManager()->data(computeCommandHandle)) != nullptr + && computeJob->isEnabled()) { + + const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); + const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId); + + // 1 RenderCommand per RenderPass pass on an Entity with a Mesh + for (const RenderPassParameterData &passData : renderPassData) { + // Add the RenderPass Parameters + RenderCommand command = {}; + RenderPass *pass = passData.pass; + + if (pass->hasRenderStates()) { + command.m_stateSet = RenderStateSetPtr::create(); + addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager()); + + // Merge per pass stateset with global stateset + // so that the local stateset only overrides + if (m_stateSet != nullptr) + command.m_stateSet->merge(m_stateSet); + command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); + } + command.m_shaderId = pass->shaderProgram(); + command.m_glShader = glShaderManager->lookupResource(command.m_shaderId); + + // It takes two frames to have a valid command as we can only + // reference a glShader at frame n if it has been loaded at frame n - 1 + if (!command.m_glShader) + continue; + + command.m_computeCommand = computeCommandHandle; + command.m_type = RenderCommand::Compute; + command.m_workGroups[0] = std::max(m_workGroups[0], computeJob->x()); + command.m_workGroups[1] = std::max(m_workGroups[1], computeJob->y()); + command.m_workGroups[2] = std::max(m_workGroups[2], computeJob->z()); + + commands.push_back(entity, + std::move(command), + std::move(passData)); + } + } + } + + return commands; +} + +void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, + int offset, + int count) +{ + // Note: since many threads can be building render commands + // we need to ensure that the UniformBlockValueBuilder they are using + // is only accessed from the same thread + UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); + builder->shaderDataManager = m_manager->shaderDataManager(); + builder->textureManager = m_manager->textureManager(); + m_localData.setLocalData(builder); + + for (int i = 0, m = count; i < m; ++i) { + const int idx = offset + i; + Entity *entity = renderCommandData->entities.at(idx); + const RenderPassParameterData passData = renderCommandData->passesData.at(idx); + RenderCommand &command = renderCommandData->commands[idx]; + + // Pick which lights to take in to account. + // For now decide based on the distance by taking the MAX_LIGHTS closest lights. + // Replace with more sophisticated mechanisms later. + // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command + QVector<LightSource> lightSources; + EnvironmentLight *environmentLight = nullptr; + + if (command.m_type == RenderCommand::Draw) { + // Project the camera-to-object-center vector onto the camera + // view vector. This gives a depth value suitable as the key + // for BackToFront sorting. + command.m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); + + environmentLight = m_environmentLight; + lightSources = m_lightSources; + + if (lightSources.size() > 1) { + const Vector3D entityCenter = entity->worldBoundingVolume()->center(); + std::sort(lightSources.begin(), lightSources.end(), + [&] (const LightSource &a, const LightSource &b) { + const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center()); + const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center()); + return distA < distB; + }); + } + lightSources = lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)); + } else { // Compute + // Note: if frameCount has reached 0 in the previous frame, isEnabled + // would be false + ComputeCommand *computeJob = m_manager->computeJobManager()->data(command.m_computeCommand); + if (computeJob->runType() == QComputeCommand::Manual) + computeJob->updateFrameCount(); + } + + ParameterInfoList globalParameters = passData.parameterInfo; + // setShaderAndUniforms can initialize a localData + // make sure this is cleared before we leave this function + + setShaderAndUniforms(&command, + globalParameters, + entity, + lightSources, + environmentLight); + } + + // We reset the local data once we are done with it + m_localData.setLocalData(nullptr); +} + +void RenderView::updateMatrices() +{ + if (m_data.m_renderCameraNode && m_data.m_renderCameraLens && m_data.m_renderCameraLens->isEnabled()) { + const Matrix4x4 cameraWorld = *(m_data.m_renderCameraNode->worldTransform()); + setViewMatrix(m_data.m_renderCameraLens->viewMatrix(cameraWorld)); + + setViewProjectionMatrix(m_data.m_renderCameraLens->projection() * viewMatrix()); + //To get the eyePosition of the camera, we need to use the inverse of the + //camera's worldTransform matrix. + const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); + const Vector3D eyePosition(inverseWorldTransform.column(3)); + setEyePosition(eyePosition); + + // Get the viewing direction of the camera. Use the normal matrix to + // ensure non-uniform scale works too. + const QMatrix3x3 normalMat = convertToQMatrix4x4(m_data.m_viewMatrix).normalMatrix(); + // dir = normalize(QVector3D(0, 0, -1) * normalMat) + setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); + } +} + +void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const +{ + // At this point a uniform value can only be a scalar type + // or a Qt3DCore::QNodeId corresponding to a Texture or Image + // ShaderData/Buffers would be handled as UBO/SSBO and would therefore + // not be in the default uniform block + if (value.valueType() == UniformValue::NodeId) { + const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>(); + + const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); + UniformValue::ValueType resourceType = UniformValue::TextureValue; + + for (int i = 0; i < uniformArraySize; ++i) { + const Qt3DCore::QNodeId resourceId = nodeIds[i]; + + const Texture *tex = m_manager->textureManager()->lookupResource(resourceId); + if (tex != nullptr) { + uniformPack.setTexture(nameId, i, resourceId); + } else { + const ShaderImage *img = m_manager->shaderImageManager()->lookupResource(resourceId); + if (img != nullptr) { + resourceType = UniformValue::ShaderImageValue; + uniformPack.setImage(nameId, i, resourceId); + } + } + } + + // This uniform will be overridden in SubmissionContext::setParameters + // and -1 values will be replaced by valid Texture or Image units + UniformValue uniformValue(uniformArraySize * sizeof(int), resourceType); + std::fill(uniformValue.data<int>(), uniformValue.data<int>() + uniformArraySize, -1); + uniformPack.setUniform(nameId, uniformValue); + } else { + uniformPack.setUniform(nameId, value); + } +} + +void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, + int glslNameId, + int nameId, + Entity *entity, + const Matrix4x4 &worldTransform) const +{ + uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform)); +} + +void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, + GLShader *shader, + const ShaderUniformBlock &block, + const UniformValue &value) const +{ + Q_UNUSED(shader) + + if (value.valueType() == UniformValue::NodeId) { + + Buffer *buffer = nullptr; + if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) { + BlockToUBO uniformBlockUBO; + uniformBlockUBO.m_blockIndex = block.m_index; + uniformBlockUBO.m_bufferID = buffer->peerId(); + uniformBlockUBO.m_needsUpdate = false; + uniformPack.setUniformBuffer(std::move(uniformBlockUBO)); + // Buffer update to GL buffer will be done at render time + } + } +} + +void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, + GLShader *shader, + const ShaderStorageBlock &block, + const UniformValue &value) const +{ + Q_UNUSED(shader) + if (value.valueType() == UniformValue::NodeId) { + Buffer *buffer = nullptr; + if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) { + BlockToSSBO shaderStorageBlock; + shaderStorageBlock.m_blockIndex = block.m_index; + shaderStorageBlock.m_bufferID = buffer->peerId(); + shaderStorageBlock.m_bindingIndex = block.m_binding; + uniformPack.setShaderStorageBuffer(shaderStorageBlock); + // Buffer update to GL buffer will be done at render time + } + } +} + +void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, GLShader *shader, ShaderData *shaderData, const QString &structName) const +{ + UniformBlockValueBuilder *builder = m_localData.localData(); + builder->activeUniformNamesToValue.clear(); + + // Set the view matrix to be used to transform "Transformed" properties in the ShaderData + builder->viewMatrix = m_data.m_viewMatrix; + // Force to update the whole block + builder->updatedPropertiesOnly = false; + // Retrieve names and description of each active uniforms in the uniform block + builder->uniforms = shader->activeUniformsForUniformBlock(-1); + // Build name-value map for the block + builder->buildActiveUniformNameValueMapStructHelper(shaderData, structName); + // Set uniform values for each entrie of the block name-value map + QHash<int, QVariant>::const_iterator activeValuesIt = builder->activeUniformNamesToValue.constBegin(); + const QHash<int, QVariant>::const_iterator activeValuesEnd = builder->activeUniformNamesToValue.constEnd(); + + // TO DO: Make the ShaderData store UniformValue + while (activeValuesIt != activeValuesEnd) { + setUniformValue(uniformPack, activeValuesIt.key(), UniformValue::fromVariant(activeValuesIt.value())); + ++activeValuesIt; + } +} + +void RenderView::setShaderAndUniforms(RenderCommand *command, + ParameterInfoList ¶meters, + Entity *entity, + const QVector<LightSource> &activeLightSources, + EnvironmentLight *environmentLight) const +{ + // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here + // Set shader, technique, and effect by basically doing : + // ShaderProgramManager[MaterialManager[frontentEntity->id()]->Effect->Techniques[TechniqueFilter->name]->RenderPasses[RenderPassFilter->name]]; + // The Renderer knows that if one of those is null, a default material / technique / effect as to be used + + // Find all RenderPasses (in order) matching values set in the RenderPassFilter + // Get list of parameters for the Material, Effect, and Technique + // For each ParameterBinder in the RenderPass -> create a QUniformPack + // Once that works, improve that to try and minimize QUniformPack updates + + GLShader *shader = command->m_glShader; + if (shader != nullptr && shader->isLoaded()) { + + // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings + // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name + // equals to the parameter name + const QVector<int> uniformNamesIds = shader->uniformsNamesIds(); + const QVector<int> standardUniformNamesIds = shader->standardUniformNameIds(); + const QVector<int> uniformBlockNamesIds = shader->uniformBlockNamesIds(); + const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds(); + const QVector<int> attributeNamesIds = shader->attributeNamesIds(); + + // Set fragData Name and index + // Later on we might want to relink the shader if attachments have changed + // But for now we set them once and for all + if (!m_renderTarget.isNull() && !shader->isLoaded()) { + QHash<QString, int> fragOutputs; + const auto atts = m_attachmentPack.attachments(); + for (const Attachment &att : atts) { + if (att.m_point <= QRenderTargetOutput::Color15) + fragOutputs.insert(att.m_name, att.m_point); + } + // Set frag outputs in the shaders if hash not empty + if (!fragOutputs.isEmpty()) + shader->setFragOutputs(fragOutputs); + } + + if (!uniformNamesIds.isEmpty() || !standardUniformNamesIds.isEmpty() || + !attributeNamesIds.isEmpty() || + !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) { + + // Set default standard uniforms without bindings + const Matrix4x4 worldTransform = *(entity->worldTransform()); + + for (const int uniformNameId : standardUniformNamesIds) + setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); + + // Set default attributes + command->m_activeAttributes = attributeNamesIds; + + // At this point we know whether the command is a valid draw command or not + // We still need to process the uniforms as the command could be a compute command + command->m_isValid = !command->m_activeAttributes.empty(); + + // Parameters remaining could be + // -> uniform scalar / vector + // -> uniform struct / arrays + // -> uniform block / array (4.3) + // -> ssbo block / array (4.3) + + ParameterInfoList::const_iterator it = parameters.cbegin(); + const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); + + while (it != parametersEnd) { + Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); + const UniformValue &uniformValue = param->uniformValue(); + if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform + setUniformValue(command->m_parameterPack, it->nameId, uniformValue); + } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block + setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue); + } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO + setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue); + } else { // Parameter is a struct + ShaderData *shaderData = nullptr; + if (uniformValue.valueType() == UniformValue::NodeId && + (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { + // Try to check if we have a struct or array matching a QShaderData parameter + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId)); + } + // Otherwise: param unused by current shader + } + ++it; + } + + // Lights + + int lightIdx = 0; + for (const LightSource &lightSource : activeLightSources) { + if (lightIdx == MAX_LIGHTS) + break; + Entity *lightEntity = lightSource.entity; + const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); + const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f); + for (Light *light : lightSource.lights) { + if (!light->isEnabled()) + continue; + + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); + if (!shaderData) + continue; + + if (lightIdx == MAX_LIGHTS) + break; + + // Note: implicit conversion of values to UniformValue + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); + + setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f); + + + // There is no risk in doing that even if multithreaded + // since we are sure that a shaderData is unique for a given light + // and won't ever be referenced as a Component either + Matrix4x4 *worldTransform = lightEntity->worldTransform(); + if (worldTransform) + shaderData->updateWorldTransform(*worldTransform); + + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]); + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); + ++lightIdx; + } + } + + if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID)) + setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx))); + + // If no active light sources and no environment light, add a default light + if (activeLightSources.isEmpty() && !environmentLight) { + // Note: implicit conversion of values to UniformValue + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); + + setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); + } + + // Environment Light + int envLightCount = 0; + if (environmentLight && environmentLight->isEnabled()) { + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); + if (shaderData) { + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); + envLightCount = 1; + } + } else { + // with some drivers, samplers (like the envbox sampler) need to be bound even though + // they may not be actually used, otherwise draw calls can fail + static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); + static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); + setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount()); + setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount()); + } + setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); + } + } +} + +bool RenderView::hasBlitFramebufferInfo() const +{ + return m_hasBlitFramebufferInfo; +} + +void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) +{ + m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; +} + +BlitFramebufferInfo RenderView::blitFrameBufferInfo() const +{ + return m_blitFrameBufferInfo; +} + +void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo) +{ + m_blitFrameBufferInfo = blitFrameBufferInfo; +} + +bool RenderView::isDownloadBuffersEnable() const +{ + return m_isDownloadBuffersEnable; +} + +void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) +{ + m_isDownloadBuffersEnable = isDownloadBuffersEnable; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h new file mode 100644 index 000000000..c12ee627a --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderview_p.h @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERVIEW_H +#define QT3DRENDER_RENDER_RENDERVIEW_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qparameter.h> +#include <Qt3DRender/qclearbuffers.h> +#include <Qt3DRender/qlayerfilter.h> +#include <Qt3DRender/private/clearbuffers_p.h> +#include <Qt3DRender/private/cameralens_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/qsortpolicy_p.h> +#include <Qt3DRender/private/lightsource_p.h> +#include <Qt3DRender/private/qmemorybarrier_p.h> +#include <Qt3DRender/private/qrendercapture_p.h> +#include <Qt3DRender/private/qblitframebuffer_p.h> +#include <Qt3DRender/private/qwaitfence_p.h> + +#include <Qt3DCore/private/qframeallocator_p.h> +#include <Qt3DRender/private/aligned_malloc_p.h> + +#include <renderer_p.h> +// TODO: Move out once this is all refactored +#include <renderviewjobutils_p.h> + +#include <QVector> +#include <QSurface> +#include <QMutex> +#include <QColor> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +class QRenderPass; + +namespace Render { + +class Renderer; +class NodeManagers; +class RenderCommand; +class RenderPassFilter; +class TechniqueFilter; +class ViewportNode; +class Effect; +class RenderPass; + +typedef QPair<ShaderUniform, QVariant> ActivePropertyContent; +typedef QPair<QString, ActivePropertyContent > ActiveProperty; + +struct Q_AUTOTEST_EXPORT ClearBufferInfo +{ + int drawBufferIndex = 0; + QRenderTargetOutput::AttachmentPoint attchmentPoint = QRenderTargetOutput::Color0; + QVector4D clearColor; +}; + +struct Q_AUTOTEST_EXPORT BlitFramebufferInfo +{ + Qt3DCore::QNodeId sourceRenderTargetId; + Qt3DCore::QNodeId destinationRenderTargetId; + QRect sourceRect; + QRect destinationRect; + Qt3DRender::QRenderTargetOutput::AttachmentPoint sourceAttachmentPoint; + Qt3DRender::QRenderTargetOutput::AttachmentPoint destinationAttachmentPoint; + QBlitFramebuffer::InterpolationMethod interpolationMethod; +}; + +// This class is kind of analogous to RenderBin but I want to avoid trampling +// on that until we get this working + +class Q_AUTOTEST_EXPORT RenderView +{ +public: + RenderView(); + ~RenderView(); + + QT3D_ALIGNED_MALLOC_AND_FREE() + + // TODO: Add a way to specify a sort predicate for the RenderCommands + void sort(); + + void setRenderer(Renderer *renderer); + inline void setSurfaceSize(const QSize &size) Q_DECL_NOTHROW { m_surfaceSize = size; } + inline Renderer *renderer() const Q_DECL_NOTHROW { return m_renderer; } + inline NodeManagers *nodeManagers() const Q_DECL_NOTHROW { return m_manager; } + inline const QSize &surfaceSize() const Q_DECL_NOTHROW { return m_surfaceSize; } + inline void setDevicePixelRatio(qreal r) Q_DECL_NOTHROW { m_devicePixelRatio = r; } + inline qreal devicePixelRatio() const Q_DECL_NOTHROW { return m_devicePixelRatio; } + + inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_data.m_renderCameraLens = renderCameraLens; } + inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_data.m_renderCameraLens; } + + inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_data.m_renderCameraNode = renderCameraNode; } + inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_data.m_renderCameraNode; } + + inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_data.m_viewMatrix = viewMatrix; } + inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; } + + inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_data.m_viewProjectionMatrix = viewProjectionMatrix; } + inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_data.m_viewProjectionMatrix; } + + inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; } + inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; } + + inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_data.m_eyeViewDir = dir; } + inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; } + + inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_data.m_layerFilterIds.push_back(layerFilterId); } + inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_data.m_layerFilterIds; } + + inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_data.m_proximityFilterIds.push_back(proximityFilterId); } + inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_data.m_proximityFilterIds; } + + inline void appendInsertFenceId(const Qt3DCore::QNodeId setFenceId) { m_insertFenceIds.push_back(setFenceId); } + // We prefix with get to avoid confusion when it is called + inline Qt3DCore::QNodeIdVector insertFenceIds() const { return m_insertFenceIds; } + + inline void appendWaitFence(const QWaitFenceData &data) { m_waitFences.push_back(data); } + inline QVector<QWaitFenceData> waitFences() const { return m_waitFences; } + + inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_data.m_passFilter = rpFilter; } + inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_data.m_passFilter; } + + inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_data.m_techniqueFilter = filter; } + inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_data.m_techniqueFilter; } + + inline RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet; } + void setStateSet(RenderStateSet *stateSet) Q_DECL_NOTHROW { m_stateSet = stateSet; } + + inline bool noDraw() const Q_DECL_NOTHROW { return m_noDraw; } + void setNoDraw(bool noDraw) Q_DECL_NOTHROW { m_noDraw = noDraw; } + + inline bool isCompute() const Q_DECL_NOTHROW { return m_compute; } + void setCompute(bool compute) Q_DECL_NOTHROW { m_compute = compute; } + + void setComputeWorkgroups(int x, int y, int z) Q_DECL_NOTHROW { m_workGroups[0] = x; m_workGroups[1] = y; m_workGroups[2] = z; } + const int *computeWorkGroups() const Q_DECL_NOTHROW { return m_workGroups; } + inline bool frustumCulling() const Q_DECL_NOTHROW { return m_frustumCulling; } + void setFrustumCulling(bool frustumCulling) Q_DECL_NOTHROW { m_frustumCulling = frustumCulling; } + + inline void setMaterialParameterTable(const MaterialParameterGathererData ¶meters) Q_DECL_NOTHROW { m_parameters = parameters; } + + // TODO: Get rid of this overly complex memory management by splitting out the + // InnerData as a RenderViewConfig struct. This can be created by setRenderViewConfigFromFrameGraphLeafNode + // and passed along with the RenderView to the functions that populate the renderview + inline void setViewport(const QRectF &vp) Q_DECL_NOTHROW { m_viewport = vp; } + inline QRectF viewport() const Q_DECL_NOTHROW { return m_viewport; } + + inline float gamma() const Q_DECL_NOTHROW { return m_gamma; } + inline void setGamma(float gamma) Q_DECL_NOTHROW { m_gamma = gamma; } + + // depth and stencil ClearBuffers are cached locally + // color ClearBuffers are collected, as there may be multiple + // color buffers to be cleared. we need to apply all these at rendering + void addClearBuffers(const ClearBuffers *cb); + inline QVector<ClearBufferInfo> specificClearColorBufferInfo() const { return m_specificClearColorBuffers; } + inline QVector<ClearBufferInfo> &specificClearColorBufferInfo() { return m_specificClearColorBuffers; } + inline ClearBufferInfo globalClearColorBufferInfo() const { return m_globalClearColorBuffer; } + + inline QClearBuffers::BufferTypeFlags clearTypes() const { return m_clearBuffer; } + inline float clearDepthValue() const { return m_clearDepthValue; } + inline int clearStencilValue() const { return m_clearStencilValue; } + + RenderPassList passesAndParameters(ParameterInfoList *parameter, Entity *node, bool useDefaultMaterials = true); + + EntityRenderCommandData buildDrawRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const; + EntityRenderCommandData buildComputeRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const; + + + void updateRenderCommand(EntityRenderCommandData *renderCommandData, + int offset, int count); + + + void setCommands(const QVector<RenderCommand> &commands) Q_DECL_NOTHROW { m_commands = commands; } + QVector<RenderCommand> &commands() { return m_commands; } + QVector<RenderCommand> commands() const { return m_commands; } + + void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; } + const AttachmentPack &attachmentPack() const { return m_attachmentPack; } + + void setRenderTargetId(Qt3DCore::QNodeId renderTargetId) Q_DECL_NOTHROW { m_renderTarget = renderTargetId; } + Qt3DCore::QNodeId renderTargetId() const Q_DECL_NOTHROW { return m_renderTarget; } + + void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_data.m_sortingTypes.append(sortTypes); } + + void setSurface(QSurface *surface) { m_surface = surface; } + QSurface *surface() const { return m_surface; } + + void setLightSources(const QVector<LightSource> &lightSources) Q_DECL_NOTHROW { m_lightSources = lightSources; } + void setEnvironmentLight(EnvironmentLight *environmentLight) Q_DECL_NOTHROW { m_environmentLight = environmentLight; } + + void updateMatrices(); + + inline void setRenderCaptureNodeId(const Qt3DCore::QNodeId nodeId) Q_DECL_NOTHROW { m_renderCaptureNodeId = nodeId; } + inline const Qt3DCore::QNodeId renderCaptureNodeId() const Q_DECL_NOTHROW { return m_renderCaptureNodeId; } + inline void setRenderCaptureRequest(const QRenderCaptureRequest& request) Q_DECL_NOTHROW { m_renderCaptureRequest = request; } + inline const QRenderCaptureRequest renderCaptureRequest() const Q_DECL_NOTHROW { return m_renderCaptureRequest; } + + void setMemoryBarrier(QMemoryBarrier::Operations barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; } + QMemoryBarrier::Operations memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; } + + // Helps making the size of RenderView smaller + // Contains all the data needed for the actual building of the RenderView + // But that aren't used later by the Renderer + struct InnerData { + InnerData() + : m_renderCameraLens(nullptr) + , m_renderCameraNode(nullptr) + , m_techniqueFilter(nullptr) + , m_passFilter(nullptr) + { + } + CameraLens *m_renderCameraLens; + Entity *m_renderCameraNode; + const TechniqueFilter *m_techniqueFilter; + const RenderPassFilter *m_passFilter; + Matrix4x4 m_viewMatrix; + Matrix4x4 m_viewProjectionMatrix; + Qt3DCore::QNodeIdVector m_layerFilterIds; + QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes; + Vector3D m_eyePos; + Vector3D m_eyeViewDir; + Qt3DCore::QNodeIdVector m_proximityFilterIds; + }; + + bool isDownloadBuffersEnable() const; + void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable); + + BlitFramebufferInfo blitFrameBufferInfo() const; + void setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo); + + bool hasBlitFramebufferInfo() const; + void setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo); + +private: + void setShaderAndUniforms(RenderCommand *command, + ParameterInfoList ¶meters, + Entity *entity, + const QVector<LightSource> &activeLightSources, + EnvironmentLight *environmentLight) const; + mutable QThreadStorage<UniformBlockValueBuilder*> m_localData; + + Qt3DCore::QNodeId m_renderCaptureNodeId; + QRenderCaptureRequest m_renderCaptureRequest; + bool m_isDownloadBuffersEnable; + + bool m_hasBlitFramebufferInfo; + BlitFramebufferInfo m_blitFrameBufferInfo; + + Renderer *m_renderer; + NodeManagers *m_manager; + QSize m_surfaceSize; + qreal m_devicePixelRatio; + + InnerData m_data; + + QRectF m_viewport; + float m_gamma; + Qt3DCore::QNodeId m_renderTarget; + QSurface *m_surface; + AttachmentPack m_attachmentPack; + QClearBuffers::BufferTypeFlags m_clearBuffer; + float m_clearDepthValue; + int m_clearStencilValue; + ClearBufferInfo m_globalClearColorBuffer; // global ClearColor + QVector<ClearBufferInfo> m_specificClearColorBuffers; // different draw buffers with distinct colors + RenderStateSet *m_stateSet; + bool m_noDraw:1; + bool m_compute:1; + bool m_frustumCulling:1; + int m_workGroups[3]; + QMemoryBarrier::Operations m_memoryBarrier; + QVector<Qt3DCore::QNodeId> m_insertFenceIds; + QVector<QWaitFenceData> m_waitFences; + + QVector<RenderCommand> m_commands; + mutable QVector<LightSource> m_lightSources; + EnvironmentLight *m_environmentLight; + + MaterialParameterGathererData m_parameters; + + enum StandardUniform + { + ModelMatrix, + ViewMatrix, + ProjectionMatrix, + ModelViewMatrix, + ViewProjectionMatrix, + ModelViewProjectionMatrix, + InverseModelMatrix, + InverseViewMatrix, + InverseProjectionMatrix, + InverseModelViewMatrix, + InverseViewProjectionMatrix, + InverseModelViewProjectionMatrix, + ModelNormalMatrix, + ModelViewNormalMatrix, + ViewportMatrix, + InverseViewportMatrix, + AspectRatio, + Time, + Exposure, + Gamma, + EyePosition, + SkinningPalette + }; + + typedef QHash<int, StandardUniform> StandardUniformsNameToTypeHash; + static StandardUniformsNameToTypeHash ms_standardUniformSetters; + static StandardUniformsNameToTypeHash initializeStandardUniformSetters(); + + UniformValue standardUniformValue(StandardUniform standardUniformType, + Entity *entity, + const Matrix4x4 &model) const; + + void setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const; + void setStandardUniformValue(ShaderParameterPack &uniformPack, + int glslNameId, + int nameId, + Entity *entity, + const Matrix4x4 &worldTransform) const; + void setUniformBlockValue(ShaderParameterPack &uniformPack, + GLShader *shader, + const ShaderUniformBlock &block, + const UniformValue &value) const; + void setShaderStorageValue(ShaderParameterPack &uniformPack, + GLShader *shader, + const ShaderStorageBlock &block, + const UniformValue &value) const; + void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, + GLShader *shader, + ShaderData *shaderData, + const QString &structName) const; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEW_H diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp new file mode 100644 index 000000000..8f1b17119 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp @@ -0,0 +1,803 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderviewbuilder_p.h" + +#include <QThread> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +// In some cases having less jobs is better (especially on fast cpus where +// splitting just adds more overhead). Ideally, we should try to set the value +// depending on the platform/CPU/nbr of cores +const int RenderViewBuilder::m_optimalParallelJobCount = QThread::idealThreadCount(); + +namespace { + +int findIdealNumberOfWorkers(int elementCount, int packetSize = 100) +{ + if (elementCount == 0 || packetSize == 0) + return 0; + return std::min(std::max(elementCount / packetSize, 1), RenderViewBuilder::optimalJobCount()); +} + + +class SyncPreCommandBuilding +{ +public: + explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_renderViewInitializer(renderViewInitializerJob) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) + , m_renderer(renderer) + , m_leafNode(leafNode) + { + } + + void operator()() + { + // Split commands to build among jobs + QMutexLocker lock(m_renderer->cache()->mutex()); + // Rebuild RenderCommands for all entities in RV (ignoring filtering) + RendererCache *cache = m_renderer->cache(); + const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; + RenderView *rv = m_renderViewInitializer->renderView(); + const auto entities = !rv->isCompute() ? cache->renderableEntities : cache->computeEntities; + + rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); + + lock.unlock(); + + // Split among the ideal number of command builders + const int idealPacketSize = std::min(std::max(100, entities.size() / RenderViewBuilder::optimalJobCount()), entities.size()); + // Try to split work into an ideal number of workers + const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize); + + for (int i = 0; i < m; ++i) { + const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i); + const int count = (i == m - 1) ? entities.size() - (i * idealPacketSize) : idealPacketSize; + renderViewCommandBuilder->setEntities(entities, i * idealPacketSize, count); + } + } + +private: + RenderViewInitializerJobPtr m_renderViewInitializer; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +class SyncRenderViewPostCommandUpdate +{ +public: + explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs, + Renderer *renderer) + : m_renderViewJob(renderViewJob) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs) + , m_renderer(renderer) + {} + + void operator()() + { + // Append all the commands and sort them + RenderView *rv = m_renderViewJob->renderView(); + + const EntityRenderCommandDataPtr commandData = m_renderViewCommandUpdaterJobs.first()->renderables(); + + if (commandData) { + const QVector<RenderCommand> commands = std::move(commandData->commands); + rv->setCommands(commands); + + // TO DO: Find way to store commands once or at least only when required + // Sort the commands + rv->sort(); + } + + // Enqueue our fully populated RenderView with the RenderThread + m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex()); + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + Renderer *m_renderer; +}; + +class SyncPreFrustumCulling +{ +public: + explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCulling) + : m_renderViewJob(renderViewJob) + , m_frustumCullingJob(frustumCulling) + {} + + void operator()() + { + RenderView *rv = m_renderViewJob->renderView(); + + // Update matrices now that all transforms have been updated + rv->updateMatrices(); + + // Frustum culling + m_frustumCullingJob->setViewProjection(rv->viewProjectionMatrix()); + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + FrustumCullingJobPtr m_frustumCullingJob; +}; + +class SyncRenderViewPostInitialization +{ +public: + explicit SyncRenderViewPostInitialization(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterLayerEntityJobPtr &filterEntityByLayerJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs) + : m_renderViewJob(renderViewJob) + , m_frustumCullingJob(frustumCullingJob) + , m_filterEntityByLayerJob(filterEntityByLayerJob) + , m_filterProximityJob(filterProximityJob) + , m_materialGathererJobs(materialGathererJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) + {} + + void operator()() + { + RenderView *rv = m_renderViewJob->renderView(); + + // Layer filtering + if (!m_filterEntityByLayerJob.isNull()) + m_filterEntityByLayerJob->setLayerFilters(rv->layerFilters()); + + // Proximity filtering + m_filterProximityJob->setProximityFilterIds(rv->proximityFilterIds()); + + // Material Parameter building + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { + materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter())); + materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter())); + } + + // Command builders and updates + for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) + renderViewCommandUpdater->setRenderView(rv); + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) + renderViewCommandBuilder->setRenderView(rv); + + // Set whether frustum culling is enabled or not + m_frustumCullingJob->setActive(rv->frustumCulling()); + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + FrustumCullingJobPtr m_frustumCullingJob; + FilterLayerEntityJobPtr m_filterEntityByLayerJob; + FilterProximityDistanceJobPtr m_filterProximityJob; + QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; +}; + +class SyncRenderViewPreCommandUpdate +{ +public: + explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, + Renderer *renderer, + FrameGraphNode *leafNode, + bool fullCommandRebuild) + : m_renderViewJob(renderViewJob) + , m_frustumCullingJob(frustumCullingJob) + , m_filterProximityJob(filterProximityJob) + , m_materialGathererJobs(materialGathererJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) + , m_renderer(renderer) + , m_leafNode(leafNode) + , m_fullRebuild(fullCommandRebuild) + {} + + void operator()() + { + // Set the result of previous job computations + // for final RenderCommand building + RenderView *rv = m_renderViewJob->renderView(); + + if (!rv->noDraw()) { + ///////// CACHE LOCKED //////////// + // Retrieve Data from Cache + RendererCache *cache = m_renderer->cache(); + QMutexLocker lock(cache->mutex()); + Q_ASSERT(cache->leafNodeCache.contains(m_leafNode)); + + const bool isDraw = !rv->isCompute(); + const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; + + // Rebuild RenderCommands if required + // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change) + // and allow to skip that step most of the time + if (m_fullRebuild) { + EntityRenderCommandData commandData; + // Reduction + { + int totalCommandCount = 0; + for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) + totalCommandCount += renderViewCommandBuilder->commandData().size(); + commandData.reserve(totalCommandCount); + for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) + commandData += std::move(renderViewCommandBuilder->commandData()); + } + + + // Store new cache + RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; + writableCacheForLeaf.renderCommandData = std::move(commandData); + } + const EntityRenderCommandData commandData = dataCacheForLeaf.renderCommandData; + const QVector<Entity *> filteredEntities = dataCacheForLeaf.filterEntitiesByLayer; + QVector<Entity *> renderableEntities = isDraw ? cache->renderableEntities : cache->computeEntities; + QVector<LightSource> lightSources = cache->gatheredLights; + + rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); + rv->setEnvironmentLight(cache->environmentLight); + lock.unlock(); + ///////// END OF CACHE LOCKED //////////// + + // Filter out entities that weren't selected by the layer filters + // Remove all entities from the compute and renderable vectors that aren't in the filtered layer vector + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, filteredEntities); + + // Set the light sources, with layer filters applied. + for (int i = 0; i < lightSources.count(); ++i) { + if (!filteredEntities.contains(lightSources[i].entity)) + lightSources.removeAt(i--); + } + rv->setLightSources(lightSources); + + if (isDraw) { + // Filter out frustum culled entity for drawable entities + if (rv->frustumCulling()) + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_frustumCullingJob->visibleEntities()); + // Filter out entities which didn't satisfy proximity filtering + if (!rv->proximityFilterIds().empty()) + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities()); + } + + // Early return in case we have nothing to filter + if (renderableEntities.size() == 0) + return; + + // Filter out Render commands for which the Entity wasn't selected because + // of frustum, proximity or layer filtering + EntityRenderCommandDataPtr filteredCommandData = EntityRenderCommandDataPtr::create(); + filteredCommandData->reserve(renderableEntities.size()); + // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted + // What we get out of EntityRenderCommandData is also sorted by Entity + auto eIt = std::cbegin(renderableEntities); + const auto eEnd = std::cend(renderableEntities); + int cIt = 0; + const int cEnd = commandData.size(); + + while (eIt != eEnd) { + const Entity *targetEntity = *eIt; + // Advance until we have commands whose Entity has a lower address + // than the selected filtered entity + while (cIt != cEnd && commandData.entities.at(cIt) < targetEntity) + ++cIt; + + // Push pointers to command data for all commands that match the + // entity + while (cIt != cEnd && commandData.entities.at(cIt) == targetEntity) { + filteredCommandData->push_back(commandData.entities.at(cIt), + commandData.commands.at(cIt), + commandData.passesData.at(cIt)); + ++cIt; + } + ++eIt; + } + + // Split among the number of command builders + // The idealPacketSize is at least 100 entities per worker + const int idealPacketSize = std::min(std::max(100, filteredCommandData->size() / RenderViewBuilder::optimalJobCount()), filteredCommandData->size()); + const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize); + + for (int i = 0; i < m; ++i) { + const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i); + const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize) : idealPacketSize; + renderViewCommandBuilder->setRenderables(filteredCommandData, i * idealPacketSize, count); + } + } + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + FrustumCullingJobPtr m_frustumCullingJob; + FilterProximityDistanceJobPtr m_filterProximityJob; + QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; + bool m_fullRebuild; +}; + +class SetClearDrawBufferIndex +{ +public: + explicit SetClearDrawBufferIndex(const RenderViewInitializerJobPtr &renderViewJob) + : m_renderViewJob(renderViewJob) + {} + + void operator()() + { + RenderView *rv = m_renderViewJob->renderView(); + QVector<ClearBufferInfo> &clearBuffersInfo = rv->specificClearColorBufferInfo(); + const AttachmentPack &attachmentPack = rv->attachmentPack(); + for (ClearBufferInfo &clearBufferInfo : clearBuffersInfo) + clearBufferInfo.drawBufferIndex = attachmentPack.getDrawBufferIndex(clearBufferInfo.attchmentPoint); + + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; +}; + +class SyncFilterEntityByLayer +{ +public: + explicit SyncFilterEntityByLayer(const FilterLayerEntityJobPtr &filterEntityByLayerJob, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_filterEntityByLayerJob(filterEntityByLayerJob) + , m_renderer(renderer) + , m_leafNode(leafNode) + { + } + + void operator()() + { + QMutexLocker lock(m_renderer->cache()->mutex()); + // Save the filtered by layer subset into the cache + const QVector<Entity *> filteredEntities = m_filterEntityByLayerJob->filteredEntities(); + RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; + dataCacheForLeaf.filterEntitiesByLayer = filteredEntities; + } + +private: + FilterLayerEntityJobPtr m_filterEntityByLayerJob; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +class SyncMaterialParameterGatherer +{ +public: + explicit SyncMaterialParameterGatherer(const QVector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_materialParameterGathererJobs(materialParameterGathererJobs) + , m_renderer(renderer) + , m_leafNode(leafNode) + { + } + + void operator()() + { + QMutexLocker lock(m_renderer->cache()->mutex()); + RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; + dataCacheForLeaf.materialParameterGatherer.clear(); + + for (const auto &materialGatherer : qAsConst(m_materialParameterGathererJobs)) + dataCacheForLeaf.materialParameterGatherer.unite(materialGatherer->materialToPassAndParameter()); + } + +private: + QVector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +} // anonymous + +RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer) + : m_leafNode(leafNode) + , m_renderViewIndex(renderViewIndex) + , m_renderer(renderer) + , m_layerCacheNeedsToBeRebuilt(false) + , m_materialGathererCacheNeedsToBeRebuilt(false) + , m_renderCommandCacheNeedsToBeRebuilt(false) + , m_renderViewJob(RenderViewInitializerJobPtr::create()) + , m_filterEntityByLayerJob() + , m_frustumCullingJob(new Render::FrustumCullingJob()) + , m_syncPreFrustumCullingJob(SynchronizerJobPtr::create(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) + , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) + , m_syncFilterEntityByLayerJob() + , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create()) +{ +} + +RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const +{ + return m_renderViewJob; +} + +FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const +{ + return m_filterEntityByLayerJob; +} + +FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const +{ + return m_frustumCullingJob; +} + +QVector<RenderViewCommandUpdaterJobPtr> RenderViewBuilder::renderViewCommandUpdaterJobs() const +{ + return m_renderViewCommandUpdaterJobs; +} + +QVector<RenderViewCommandBuilderJobPtr> RenderViewBuilder::renderViewCommandBuilderJobs() const +{ + return m_renderViewCommandBuilderJobs; +} + +QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const +{ + return m_materialGathererJobs; +} + +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const +{ + return m_syncRenderViewPostInitializationJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const +{ + return m_syncPreFrustumCullingJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const +{ + return m_syncRenderViewPreCommandBuildingJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const +{ + return m_syncRenderViewPreCommandUpdateJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const +{ + return m_syncRenderViewPostCommandUpdateJob; +} + +SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const +{ + return m_setClearDrawBufferIndexJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const +{ + return m_syncFilterEntityByLayerJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const +{ + return m_syncMaterialGathererJob; +} + +FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const +{ + return m_filterProximityJob; +} + +void RenderViewBuilder::prepareJobs() +{ + // Init what we can here + m_filterProximityJob->setManager(m_renderer->nodeManagers()); + m_frustumCullingJob->setRoot(m_renderer->sceneRoot()); + + if (m_renderCommandCacheNeedsToBeRebuilt) { + + m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { + auto renderViewCommandBuilder = Render::RenderViewCommandBuilderJobPtr::create(); + m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder); + } + m_syncRenderViewPreCommandBuildingJob = SynchronizerJobPtr::create(SyncPreCommandBuilding(m_renderViewJob, + m_renderViewCommandBuilderJobs, + m_renderer, + m_leafNode), + JobTypes::SyncRenderViewPreCommandBuilding); + } + + m_renderViewJob->setRenderer(m_renderer); + m_renderViewJob->setFrameGraphLeafNode(m_leafNode); + m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex); + + // RenderCommand building is the most consuming task -> split it + // Estimate the number of jobs to create based on the number of entities + m_renderViewCommandUpdaterJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { + auto renderViewCommandUpdater = Render::RenderViewCommandUpdaterJobPtr::create(); + renderViewCommandUpdater->setRenderer(m_renderer); + m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater); + } + + if (m_materialGathererCacheNeedsToBeRebuilt) { + // Since Material gathering is an heavy task, we split it + const QVector<HMaterial> materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles(); + const int elementsPerJob = materialHandles.size() / RenderViewBuilder::m_optimalParallelJobCount; + const int lastRemaingElements = materialHandles.size() % RenderViewBuilder::m_optimalParallelJobCount; + m_materialGathererJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { + auto materialGatherer = Render::MaterialParameterGathererJobPtr::create(); + materialGatherer->setNodeManagers(m_renderer->nodeManagers()); + if (i == RenderViewBuilder::m_optimalParallelJobCount - 1) + materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob + lastRemaingElements)); + else + materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob)); + m_materialGathererJobs.push_back(materialGatherer); + } + m_syncMaterialGathererJob = SynchronizerJobPtr::create(SyncMaterialParameterGatherer(m_materialGathererJobs, + m_renderer, + m_leafNode), + JobTypes::SyncMaterialGatherer); + } + + if (m_layerCacheNeedsToBeRebuilt) { + m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create(); + m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers()); + m_syncFilterEntityByLayerJob = SynchronizerJobPtr::create(SyncFilterEntityByLayer(m_filterEntityByLayerJob, + m_renderer, + m_leafNode), + JobTypes::SyncFilterEntityByLayer); + } + + m_syncRenderViewPreCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPreCommandUpdate(m_renderViewJob, + m_frustumCullingJob, + m_filterProximityJob, + m_materialGathererJobs, + m_renderViewCommandUpdaterJobs, + m_renderViewCommandBuilderJobs, + m_renderer, + m_leafNode, + m_renderCommandCacheNeedsToBeRebuilt), + JobTypes::SyncRenderViewPreCommandUpdate); + + m_syncRenderViewPostCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPostCommandUpdate(m_renderViewJob, + m_renderViewCommandUpdaterJobs, + m_renderer), + JobTypes::SyncRenderViewPostCommandUpdate); + + m_syncRenderViewPostInitializationJob = SynchronizerJobPtr::create(SyncRenderViewPostInitialization(m_renderViewJob, + m_frustumCullingJob, + m_filterEntityByLayerJob, + m_filterProximityJob, + m_materialGathererJobs, + m_renderViewCommandUpdaterJobs, + m_renderViewCommandBuilderJobs), + JobTypes::SyncRenderViewInitialization); +} + +QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const +{ + QVector<Qt3DCore::QAspectJobPtr> jobs; + + jobs.reserve(m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11); + + // Set dependencies + + // Finish the skinning palette job before processing renderviews + // TODO: Maybe only update skinning palettes for non-culled entities + m_renderViewJob->addDependency(m_renderer->updateSkinningPaletteJob()); + + m_syncPreFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob()); + m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob()); + m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob); + + m_frustumCullingJob->addDependency(m_renderer->expandBoundingVolumeJob()); + m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob); + + m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob); + + m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob); + + m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob()); + m_filterProximityJob->addDependency(m_syncRenderViewPostInitializationJob); + + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncRenderViewPostInitializationJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_filterProximityJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_frustumCullingJob); + + // Ensure the RenderThread won't be able to process dirtyResources + // before they have been completely gathered + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->introspectShadersJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->bufferGathererJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->textureGathererJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->cacheLightJob()); + + for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) { + renderViewCommandUpdater->addDependency(m_syncRenderViewPreCommandUpdateJob); + m_syncRenderViewPostCommandUpdateJob->addDependency(renderViewCommandUpdater); + } + + m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewPostCommandUpdateJob); + m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob); + + // Add jobs + jobs.push_back(m_renderViewJob); // Step 1 + + jobs.push_back(m_syncRenderViewPostInitializationJob); // Step 2 + + if (m_renderCommandCacheNeedsToBeRebuilt) { // Step 3 + m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheComputableEntitiesJob()); + m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheRenderableEntitiesJob()); + m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncRenderViewPostInitializationJob); + + if (m_materialGathererCacheNeedsToBeRebuilt) + m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncMaterialGathererJob); + + jobs.push_back(m_syncRenderViewPreCommandBuildingJob); + + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) { + renderViewCommandBuilder->addDependency(m_syncRenderViewPreCommandBuildingJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(renderViewCommandBuilder); + jobs.push_back(renderViewCommandBuilder); + } + } + + if (m_layerCacheNeedsToBeRebuilt) { + m_filterEntityByLayerJob->addDependency(m_renderer->updateEntityLayersJob()); + m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob); + m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob()); + + m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncFilterEntityByLayerJob); + + jobs.push_back(m_filterEntityByLayerJob); // Step 3 + jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4 + } + jobs.push_back(m_syncPreFrustumCullingJob); // Step 3 + jobs.push_back(m_filterProximityJob); // Step 3 + jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3 + + if (m_materialGathererCacheNeedsToBeRebuilt) { + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { + materialGatherer->addDependency(m_syncRenderViewPostInitializationJob); + materialGatherer->addDependency(m_renderer->introspectShadersJob()); + materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob()); + jobs.push_back(materialGatherer); // Step3 + m_syncMaterialGathererJob->addDependency(materialGatherer); + } + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncMaterialGathererJob); + + jobs.push_back(m_syncMaterialGathererJob); // Step 3 + } + + jobs.push_back(m_frustumCullingJob); // Step 4 + jobs.push_back(m_syncRenderViewPreCommandUpdateJob); // Step 5 + + // Build RenderCommands or Update RenderCommand Uniforms + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) // Step 6 + jobs.push_back(renderViewCommandBuilder); + + jobs.push_back(m_syncRenderViewPostCommandUpdateJob); // Step 7 + + return jobs; +} + +Renderer *RenderViewBuilder::renderer() const +{ + return m_renderer; +} + +int RenderViewBuilder::renderViewIndex() const +{ + return m_renderViewIndex; +} + +void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt) +{ + m_layerCacheNeedsToBeRebuilt = needsToBeRebuilt; +} + +bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const +{ + return m_layerCacheNeedsToBeRebuilt; +} + +void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt) +{ + m_materialGathererCacheNeedsToBeRebuilt = needsToBeRebuilt; +} + +bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const +{ + return m_materialGathererCacheNeedsToBeRebuilt; +} + +void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt) +{ + m_renderCommandCacheNeedsToBeRebuilt = needsToBeRebuilt; +} + +bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const +{ + return m_renderCommandCacheNeedsToBeRebuilt; +} + +int RenderViewBuilder::optimalJobCount() +{ + return RenderViewBuilder::m_optimalParallelJobCount; +} + +QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset) +{ + QVector<Entity *> intersection; + intersection.reserve(qMin(entities.size(), subset.size())); + std::set_intersection(entities.begin(), entities.end(), + subset.begin(), subset.end(), + std::back_inserter(intersection)); + + return intersection; +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h new file mode 100644 index 000000000..71719abee --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERVIEWBUILDER_H +#define QT3DRENDER_RENDER_RENDERVIEWBUILDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <functional> +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/filterlayerentityjob_p.h> +#include <Qt3DRender/private/genericlambdajob_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/frustumcullingjob_p.h> +#include <Qt3DRender/private/filterproximitydistancejob_p.h> +#include <renderviewcommandbuilderjob_p.h> +#include <renderviewcommandupdaterjob_p.h> +#include <materialparametergathererjob_p.h> +#include <renderviewbuilderjob_p.h> +#include <renderview_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Renderer; + +using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; + +class Q_AUTOTEST_EXPORT RenderViewBuilder +{ +public: + explicit RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer); + + RenderViewInitializerJobPtr renderViewJob() const; + FilterLayerEntityJobPtr filterEntityByLayerJob() const; + FrustumCullingJobPtr frustumCullingJob() const; + QVector<RenderViewCommandBuilderJobPtr> renderViewCommandBuilderJobs() const; + QVector<RenderViewCommandUpdaterJobPtr> renderViewCommandUpdaterJobs() const; + QVector<MaterialParameterGathererJobPtr> materialGathererJobs() const; + SynchronizerJobPtr syncRenderViewPostInitializationJob() const; + SynchronizerJobPtr syncPreFrustumCullingJob() const; + SynchronizerJobPtr syncRenderViewPreCommandBuildingJob() const; + SynchronizerJobPtr syncRenderViewPreCommandUpdateJob() const; + SynchronizerJobPtr syncRenderViewPostCommandUpdateJob() const; + SynchronizerJobPtr setClearDrawBufferIndexJob() const; + SynchronizerJobPtr syncFilterEntityByLayerJob() const; + FilterProximityDistanceJobPtr filterProximityJob() const; + SynchronizerJobPtr syncMaterialGathererJob() const; + + void prepareJobs(); + QVector<Qt3DCore::QAspectJobPtr> buildJobHierachy() const; + + Renderer *renderer() const; + int renderViewIndex() const; + + void setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt); + bool layerCacheNeedsToBeRebuilt() const; + void setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt); + bool materialGathererCacheNeedsToBeRebuilt() const; + void setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt); + bool renderCommandCacheNeedsToBeRebuilt() const; + + static int optimalJobCount(); + static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset); + +private: + Render::FrameGraphNode *m_leafNode; + const int m_renderViewIndex; + Renderer *m_renderer; + bool m_layerCacheNeedsToBeRebuilt; + bool m_materialGathererCacheNeedsToBeRebuilt; + bool m_renderCommandCacheNeedsToBeRebuilt; + + RenderViewInitializerJobPtr m_renderViewJob; + FilterLayerEntityJobPtr m_filterEntityByLayerJob; + FrustumCullingJobPtr m_frustumCullingJob; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; + + SynchronizerJobPtr m_syncRenderViewPostInitializationJob; + SynchronizerJobPtr m_syncPreFrustumCullingJob; + SynchronizerJobPtr m_syncRenderViewPreCommandBuildingJob; + SynchronizerJobPtr m_syncRenderViewPreCommandUpdateJob; + SynchronizerJobPtr m_syncRenderViewPostCommandUpdateJob; + SynchronizerJobPtr m_setClearDrawBufferIndexJob; + SynchronizerJobPtr m_syncFilterEntityByLayerJob; + SynchronizerJobPtr m_syncMaterialGathererJob; + FilterProximityDistanceJobPtr m_filterProximityJob; + + static const int m_optimalParallelJobCount; +}; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEWBUILDER_H diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp new file mode 100644 index 000000000..e3cc7baf7 --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shaderparameterpack_p.h" + +#include <graphicscontext_p.h> +#include <Qt3DRender/private/texture_p.h> + +#include <Qt3DCore/private/qframeallocator_p.h> + +#include <QOpenGLShaderProgram> +#include <QDebug> +#include <QColor> +#include <QQuaternion> +#include <Qt3DRender/private/renderlogging_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +ShaderParameterPack::~ShaderParameterPack() +{ +} + +void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val) +{ + m_uniforms.insert(glslNameId, val); +} + +void ShaderParameterPack::setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId texId) +{ + for (int t=0; t<m_textures.size(); ++t) { + if (m_textures[t].glslNameId != glslNameId || m_textures[t].uniformArrayIndex != uniformArrayIndex) + continue; + + m_textures[t].nodeId = texId; + return; + } + + m_textures.append(NamedResource(glslNameId, texId, uniformArrayIndex, NamedResource::Texture)); +} + +void ShaderParameterPack::setImage(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id) +{ + for (int i=0, m = m_images.size(); i < m; ++i) { + if (m_images[i].glslNameId != glslNameId || m_images[i].uniformArrayIndex != uniformArrayIndex) + continue; + + m_images[i].nodeId = id; + return; + } + + m_images.append(NamedResource(glslNameId, id, uniformArrayIndex, NamedResource::Image)); +} + +// Contains Uniform Block Index and QNodeId of the ShaderData (UBO) +void ShaderParameterPack::setUniformBuffer(BlockToUBO blockToUBO) +{ + m_uniformBuffers.append(std::move(blockToUBO)); +} + +void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO) +{ + m_shaderStorageBuffers.push_back(std::move(blockToSSBO)); +} + +void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform) +{ + m_submissionUniforms.push_back(uniform); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h new file mode 100644 index 000000000..045081afd --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H +#define QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QVariant> +#include <QByteArray> +#include <QVector> +#include <QOpenGLShaderProgram> +#include <Qt3DCore/qnodeid.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/uniform_p.h> +#include <shadervariables_p.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; + +namespace Qt3DCore { +class QFrameAllocator; +} + +namespace Qt3DRender { +namespace Render { + +class GraphicsContext; + +struct BlockToUBO { + int m_blockIndex; + Qt3DCore::QNodeId m_bufferID; + bool m_needsUpdate; + QHash<QString, QVariant> m_updatedProperties; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToUBO, Q_MOVABLE_TYPE) + +struct BlockToSSBO { + int m_blockIndex; + int m_bindingIndex; + Qt3DCore::QNodeId m_bufferID; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToSSBO, Q_PRIMITIVE_TYPE) + + +struct PackUniformHash +{ + QVector<int> keys; + QVector<UniformValue> values; + + PackUniformHash() + { + keys.reserve(10); + values.reserve(10); + } + + void insert(int key, const UniformValue &value) + { + const int idx = keys.indexOf(key); + if (idx != -1) { + values[idx] = value; + } else { + keys.push_back(key); + values.push_back(value); + } + } + + UniformValue value(int key) const + { + const int idx = keys.indexOf(key); + if (idx != -1) + return values.at(idx); + return UniformValue(); + } + + UniformValue& value(int key) + { + const int idx = keys.indexOf(key); + if (idx != -1) + return values[idx]; + insert(key, UniformValue()); + return value(key); + } + + void erase(int idx) + { + keys.removeAt(idx); + values.removeAt(idx); + } + + bool contains(int key) const + { + return keys.contains(key); + } +}; + +class Q_AUTOTEST_EXPORT ShaderParameterPack +{ +public: + ~ShaderParameterPack(); + + void setUniform(const int glslNameId, const UniformValue &val); + void setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id); + void setImage(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id); + + void setUniformBuffer(BlockToUBO blockToUBO); + void setShaderStorageBuffer(BlockToSSBO blockToSSBO); + void setSubmissionUniform(const ShaderUniform &uniform); + + inline PackUniformHash &uniforms() { return m_uniforms; } + inline const PackUniformHash &uniforms() const { return m_uniforms; } + UniformValue uniform(const int glslNameId) const { return m_uniforms.value(glslNameId); } + + + struct NamedResource + { + enum Type { + Texture = 0, + Image + }; + + NamedResource() {} + NamedResource(const int glslNameId, Qt3DCore::QNodeId texId, + int uniformArrayIndex, Type type) + : glslNameId(glslNameId) + , nodeId(texId) + , uniformArrayIndex(uniformArrayIndex) + , type(type) + { } + + int glslNameId; + Qt3DCore::QNodeId nodeId; + int uniformArrayIndex; + Type type; + + bool operator==(const NamedResource &other) const + { + return glslNameId == other.glslNameId && + nodeId == other.nodeId && + uniformArrayIndex == other.uniformArrayIndex && + type == other.type; + } + + bool operator!=(const NamedResource &other) const + { + return !(*this == other); + } + }; + + inline QVector<NamedResource> textures() const { return m_textures; } + inline QVector<NamedResource> images() const { return m_images; } + inline QVector<BlockToUBO> uniformBuffers() const { return m_uniformBuffers; } + inline QVector<BlockToSSBO> shaderStorageBuffers() const { return m_shaderStorageBuffers; } + inline QVector<ShaderUniform> submissionUniforms() const { return m_submissionUniforms; } +private: + PackUniformHash m_uniforms; + + QVector<NamedResource> m_textures; + QVector<NamedResource> m_images; + QVector<BlockToUBO> m_uniformBuffers; + QVector<BlockToSSBO> m_shaderStorageBuffers; + QVector<ShaderUniform> m_submissionUniforms; + + friend class RenderView; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderParameterPack::NamedResource, Q_PRIMITIVE_TYPE) + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Qt3DRender::Render::ShaderParameterPack) + +#endif // QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H diff --git a/src/plugins/renderers/opengl/renderer/shadervariables_p.h b/src/plugins/renderers/opengl/renderer/shadervariables_p.h new file mode 100644 index 000000000..e0fa07dff --- /dev/null +++ b/src/plugins/renderers/opengl/renderer/shadervariables_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_SHADERVARIABLES_P_H +#define QT3DRENDER_RENDER_SHADERVARIABLES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qt3drender_global.h> +#include <QOpenGLContext> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +struct ShaderAttribute +{ + ShaderAttribute() + : m_nameId(-1) + , m_type(0) + , m_size(0) + , m_location(-1) + {} + + QString m_name; + int m_nameId; + GLenum m_type; + int m_size; + int m_location; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderAttribute, Q_MOVABLE_TYPE) + +struct ShaderUniform +{ + ShaderUniform() + : m_nameId(-1) + , m_type(GL_NONE) + , m_size(0) + , m_offset(-1) + , m_location(-1) + , m_blockIndex(-1) + , m_arrayStride(-1) + , m_matrixStride(-1) + , m_rawByteSize(0) + {} + + QString m_name; + int m_nameId; + GLenum m_type; + int m_size; + int m_offset; // -1 default, >= 0 if uniform defined in uniform block + int m_location; // -1 if uniform defined in a uniform block + int m_blockIndex; // -1 is the default, >= 0 if uniform defined in uniform block + int m_arrayStride; // -1 is the default, >= 0 if uniform defined in uniform block and if it's an array + int m_matrixStride; // -1 is the default, >= 0 uniform defined in uniform block and is a matrix + uint m_rawByteSize; // contains byte size (size / type / strides) + // size, offset and strides are in bytes +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniform, Q_MOVABLE_TYPE) + +struct ShaderUniformBlock +{ + ShaderUniformBlock() + : m_nameId(-1) + , m_index(-1) + , m_binding(-1) + , m_activeUniformsCount(0) + , m_size(0) + {} + + QString m_name; + int m_nameId; + int m_index; + int m_binding; + int m_activeUniformsCount; + int m_size; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniformBlock, Q_MOVABLE_TYPE) + +struct ShaderStorageBlock +{ + ShaderStorageBlock() + : m_nameId(-1) + , m_index(-1) + , m_binding(-1) + , m_size(0) + , m_activeVariablesCount(0) + {} + + QString m_name; + int m_nameId; + int m_index; + int m_binding; + int m_size; + int m_activeVariablesCount; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderStorageBlock, Q_MOVABLE_TYPE) + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_SHADERVARIABLES_P_H diff --git a/src/plugins/renderers/opengl/textures/gltexture.cpp b/src/plugins/renderers/opengl/textures/gltexture.cpp new file mode 100644 index 000000000..c79961d78 --- /dev/null +++ b/src/plugins/renderers/opengl/textures/gltexture.cpp @@ -0,0 +1,772 @@ +/**************************************************************************** +** +** 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 <private/qdebug_p.h> +#include <private/qopengltexture_p.h> +#include <private/qopengltexturehelper_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/qabstracttexture_p.h> +#include <Qt3DRender/private/qtextureimagedata_p.h> +#include <renderbuffer_p.h> + +#if !defined(QT_OPENGL_ES_2) +#include <QOpenGLFunctions_3_1> +#include <QOpenGLFunctions_4_5_Core> +#endif + +QT_BEGIN_NAMESPACE + +using namespace Qt3DCore; + +namespace Qt3DRender { +namespace Render { + +namespace { + +// This uploadGLData where the data is a fullsize subimage +// as QOpenGLTexture doesn't allow partial subimage uploads +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); + } +} + +// For partial sub image uploads +void uploadGLData(QOpenGLTexture *glTex, + int mipLevel, int layer, QOpenGLTexture::CubeMapFace cubeFace, + int xOffset, int yOffset, int zOffset, + const QByteArray &bytes, const QTextureImageDataPtr &data) +{ + if (data->isCompressed()) { + qWarning() << Q_FUNC_INFO << "Uploading non full sized Compressed Data not supported yet"; + } else { + QOpenGLPixelTransferOptions uploadOptions; + uploadOptions.setAlignment(1); + glTex->setData(xOffset, yOffset, zOffset, + data->width(), data->height(), data->depth(), + mipLevel, layer, cubeFace, data->layers(), + data->pixelFormat(), data->pixelType(), + bytes.constData(), &uploadOptions); + } +} + +} // anonymous + + +GLTexture::GLTexture() + : m_dirtyFlags(None) + , m_gl(nullptr) + , m_renderBuffer(nullptr) + , m_dataFunctor() + , m_pendingDataFunctor(nullptr) + , m_sharedTextureId(-1) + , m_externalRendering(false) + , m_wasTextureRecreated(false) +{ +} + +GLTexture::~GLTexture() +{ +} + +// Must be called from RenderThread with active GL context +void GLTexture::destroy() +{ + delete m_gl; + m_gl = nullptr; + delete m_renderBuffer; + m_renderBuffer = nullptr; + + m_dirtyFlags = None; + m_sharedTextureId = -1; + m_externalRendering = false; + m_wasTextureRecreated = false; + m_dataFunctor.reset(); + m_pendingDataFunctor = nullptr; + + m_properties = {}; + m_parameters = {}; + m_textureData.reset(); + m_images.clear(); + m_imageData.clear(); + m_pendingTextureDataUpdates.clear(); +} + +bool GLTexture::loadTextureDataFromGenerator() +{ + m_textureData = m_dataFunctor->operator()(); + // if there is a texture generator, most properties will be defined by it + if (m_textureData) { + const QAbstractTexture::Target target = m_textureData->target(); + + // If both target and functor return Automatic we are still + // probably loading the texture, return false + if (m_properties.target == QAbstractTexture::TargetAutomatic && + target == QAbstractTexture::TargetAutomatic) { + m_textureData.reset(); + return false; + } + + if (m_properties.target != QAbstractTexture::TargetAutomatic && + target != QAbstractTexture::TargetAutomatic && + m_properties.target != target) { + qWarning() << Q_FUNC_INFO << "Generator and Properties not requesting the same texture target"; + m_textureData.reset(); + return false; + } + + // We take target type from generator if it wasn't explicitly set by the user + if (m_properties.target == QAbstractTexture::TargetAutomatic) + m_properties.target = target; + m_properties.width = m_textureData->width(); + m_properties.height = m_textureData->height(); + m_properties.depth = m_textureData->depth(); + m_properties.layers = m_textureData->layers(); + m_properties.format = m_textureData->format(); + + const QVector<QTextureImageDataPtr> imageData = m_textureData->imageData(); + + if (imageData.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(); + } + } + return !m_textureData.isNull(); +} + +void GLTexture::loadTextureDataFromImages() +{ + int maxMipLevel = 0; + for (const Image &img : qAsConst(m_images)) { + const QTextureImageDataPtr imgData = img.generator->operator()(); + // imgData may be null in the following cases: + // - Texture is created with TextureImages which have yet to be + // loaded (skybox where you don't yet know the path, source set by + // a property binding, queued connection ...) + // - TextureImage whose generator failed to return a valid data + // (invalid url, error opening file...) + if (imgData.isNull()) + continue; + + m_imageData.push_back(imgData); + maxMipLevel = qMax(maxMipLevel, img.mipLevel); + + // If the texture doesn't have a texture generator, we will + // derive some properties from the first TextureImage (layer=0, miplvl=0, face=0) + if (!m_textureData && img.layer == 0 && img.mipLevel == 0 && img.face == QAbstractTexture::CubeMapPositiveX) { + if (imgData->width() != -1 && imgData->height() != -1 && imgData->depth() != -1) { + m_properties.width = imgData->width(); + m_properties.height = imgData->height(); + m_properties.depth = imgData->depth(); + } + // Set the format of the texture if the texture format is set to Automatic + if (m_properties.format == QAbstractTexture::Automatic) { + m_properties.format = static_cast<QAbstractTexture::TextureFormat>(imgData->format()); + } + setDirtyFlag(Properties, true); + } + } + + // make sure the number of mip levels is set when there is no texture data generator + if (!m_dataFunctor) { + m_properties.mipLevels = maxMipLevel + 1; + setDirtyFlag(Properties, true); + } +} + +// Called from RenderThread +GLTexture::TextureUpdateInfo GLTexture::createOrUpdateGLTexture() +{ + TextureUpdateInfo textureInfo; + m_wasTextureRecreated = false; + + const bool hasSharedTextureId = m_sharedTextureId > 0; + // Only load texture data if we are not using a sharedTextureId + // Check if dataFunctor or images have changed + if (!hasSharedTextureId) { + // If dataFunctor exists and we have no data and it hasnĀ“t run yet + if (m_dataFunctor && !m_textureData && m_dataFunctor.get() != m_pendingDataFunctor ) { + const bool successfullyLoadedTextureData = loadTextureDataFromGenerator(); + // If successful, m_textureData has content + if (successfullyLoadedTextureData) { + setDirtyFlag(Properties, true); + setDirtyFlag(TextureData, true); + } else { + if (m_pendingDataFunctor != m_dataFunctor.get()) { + qWarning() << "[Qt3DRender::GLTexture] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame"; + m_pendingDataFunctor = m_dataFunctor.get(); + } + textureInfo.properties.status = QAbstractTexture::Loading; + return textureInfo; + } + } + + // If images have changed, clear previous images data + // and regenerate m_imageData for the images + if (testDirtyFlag(TextureImageData)) { + m_imageData.clear(); + loadTextureDataFromImages(); + // Mark for upload if we actually have something to upload + if (!m_imageData.empty()) { + setDirtyFlag(TextureData, true); + } + // Reset image flag + setDirtyFlag(TextureImageData, false); + } + + // Don't try to create the texture if the target or format was still not set + // Format should either be set by user or if Automatic + // by either the dataGenerator of the texture or the first Image + // Target should explicitly be set by the user or the dataGenerator + if (m_properties.target == QAbstractTexture::TargetAutomatic || + m_properties.format == QAbstractTexture::Automatic || + m_properties.format == QAbstractTexture::NoFormat) { + textureInfo.properties.status = QAbstractTexture::Error; + return textureInfo; + } + } + + // If the properties changed or texture has become a shared texture from a + // 3rd party engine, we need to destroy and maybe re-allocate the texture + if (testDirtyFlag(Properties) || testDirtyFlag(SharedTextureId)) { + delete m_gl; + m_gl = nullptr; + textureInfo.wasUpdated = true; + // If we are destroyed because of some property change but still have (some) of + // our content data make sure we are marked for upload + // TO DO: We should actually check if the textureData is still correct + // in regard to the size, target and format of the texture though. + if (!testDirtyFlag(SharedTextureId) && + (m_textureData || !m_imageData.empty() || !m_pendingTextureDataUpdates.empty())) + setDirtyFlag(TextureData, true); + } + + m_properties.status = QAbstractTexture::Ready; + + if (testDirtyFlag(SharedTextureId) || hasSharedTextureId) { + // Update m_properties by doing introspection on the texture + if (hasSharedTextureId) + introspectPropertiesFromSharedTextureId(); + setDirtyFlag(SharedTextureId, false); + } else { + // We only build a QOpenGLTexture if we have no shared textureId set + if (!m_gl) { + m_gl = buildGLTexture(); + if (!m_gl) { + qWarning() << "[Qt3DRender::GLTexture] failed to create texture"; + textureInfo.properties.status = QAbstractTexture::Error; + return textureInfo; + } + + m_gl->allocateStorage(); + if (!m_gl->isStorageAllocated()) { + qWarning() << "[Qt3DRender::GLTexture] failed to allocate texture"; + textureInfo.properties.status = QAbstractTexture::Error; + return textureInfo; + } + m_wasTextureRecreated = true; + } + + textureInfo.texture = m_gl; + + // need to (re-)upload texture data? + const bool needsUpload = testDirtyFlag(TextureData); + if (needsUpload) { + uploadGLTextureData(); + setDirtyFlag(TextureData, false); + } + + // need to set texture parameters? + if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) { + updateGLTextureParameters(); + setDirtyFlag(Properties, false); + setDirtyFlag(Parameters, false); + } + } + + textureInfo.properties = m_properties; + + return textureInfo; +} + +RenderBuffer *GLTexture::getOrCreateRenderBuffer() +{ + if (m_dataFunctor && !m_textureData) { + m_textureData = m_dataFunctor->operator()(); + 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 { + if (m_pendingDataFunctor != m_dataFunctor.get()) { + qWarning() << "[Qt3DRender::GLTexture] [renderbuffer] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame"; + m_pendingDataFunctor = m_dataFunctor.get(); + } + return nullptr; + } + } + + if (testDirtyFlag(Properties)) { + delete m_renderBuffer; + m_renderBuffer = nullptr; + } + + if (!m_renderBuffer) + m_renderBuffer = new RenderBuffer(m_properties.width, m_properties.height, m_properties.format); + + setDirtyFlag(Properties, false); + setDirtyFlag(Parameters, false); + + return m_renderBuffer; +} + +// This must be called from the RenderThread +// So GLTexture release from the manager can only be done from that thread +void GLTexture::cleanup() +{ + destroy(); +} + +void GLTexture::setParameters(const TextureParameters ¶ms) +{ + if (m_parameters != params) { + m_parameters = params; + setDirtyFlag(Parameters); + } +} + +void GLTexture::setProperties(const TextureProperties &props) +{ + if (m_properties != props) { + m_properties = props; + 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; + requestImageUpload(); + } +} + +void GLTexture::setGenerator(const QTextureGeneratorPtr &generator) +{ + m_textureData.reset(); + m_dataFunctor = generator; + m_pendingDataFunctor = nullptr; + requestUpload(); +} + +void GLTexture::setSharedTextureId(int textureId) +{ + if (m_sharedTextureId != textureId) { + m_sharedTextureId = textureId; + setDirtyFlag(SharedTextureId); + } +} + +void GLTexture::addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates) +{ + m_pendingTextureDataUpdates += updates; + 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; + } + + const QAbstractTexture::Target actualTarget = m_properties.target; + if (actualTarget == QAbstractTexture::TargetAutomatic) { + // If the target is automatic at this point, it means that the texture + // hasn't been loaded yet (case of remote urls) and that loading failed + // and that target format couldn't be deduced + return nullptr; + } + + QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(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 QAbstractTexture::RGBA8_UNorm: + case QAbstractTexture::RGBAFormat: + format = QAbstractTexture::RGBAFormat; + break; + case QAbstractTexture::RGB8_UNorm: + case QAbstractTexture::RGBFormat: + format = QAbstractTexture::RGBFormat; + break; + case QAbstractTexture::DepthFormat: + format = QAbstractTexture::DepthFormat; + break; + default: + auto warning = qWarning(); + warning << "Could not find a matching OpenGL ES 2.0 texture format:"; + QtDebugUtils::formatQEnum(warning, m_properties.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 (actualTarget == QAbstractTexture::Target1DArray || + actualTarget == QAbstractTexture::Target2DArray || + actualTarget == QAbstractTexture::Target2DMultisampleArray || + actualTarget == QAbstractTexture::TargetCubeMapArray) { + glTex->setLayers(m_properties.layers); + } + + if (actualTarget == QAbstractTexture::Target2DMultisample || + 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); + if (glTex->hasFeature(QOpenGLTexture::TextureMipMapLevel)) { + 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; +} + +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 < std::min(m_images.size(), m_imageData.size()); i++) { + const QTextureImageDataPtr &imgData = m_imageData.at(i); + // Here the bytes in the QTextureImageData contain data for a single + // layer, face or mip level, unlike the QTextureGenerator case where + // they are in a single blob. Hence QTextureImageData::data() is not suitable. + const QByteArray bytes(QTextureImageDataPrivate::get(imgData.get())->m_data); + uploadGLData(m_gl, m_images[i].mipLevel, m_images[i].layer, + static_cast<QOpenGLTexture::CubeMapFace>(m_images[i].face), + bytes, imgData); + } + // Free up image data once content has been uploaded + // Note: if data functor stores the data, this won't really free anything though + m_imageData.clear(); + + // Update data from TextureUpdates + const QVector<QTextureDataUpdate> textureDataUpdates = std::move(m_pendingTextureDataUpdates); + for (const QTextureDataUpdate &update : textureDataUpdates) { + const QTextureImageDataPtr imgData = update.data(); + + if (!imgData) { + qWarning() << Q_FUNC_INFO << "QTextureDataUpdate no QTextureImageData set"; + continue; + } + + const int xOffset = update.x(); + const int yOffset = update.y(); + const int zOffset = update.z(); + const int xExtent = xOffset + imgData->width(); + const int yExtent = yOffset + imgData->height(); + const int zExtent = zOffset + imgData->depth(); + + // Check update is compatible with our texture + if (xOffset >= m_gl->width() || + yOffset >= m_gl->height() || + zOffset >= m_gl->depth() || + xExtent > m_gl->width() || + yExtent > m_gl->height() || + zExtent > m_gl->depth() || + update.mipLevel() >= m_gl->mipLevels() || + update.layer() >= m_gl->layers()) { + qWarning() << Q_FUNC_INFO << "QTextureDataUpdate incompatible with texture"; + continue; + } + + const QByteArray bytes = (QTextureImageDataPrivate::get(imgData.get())->m_data); + // Here the bytes in the QTextureImageData contain data for a single + // layer, face or mip level, unlike the QTextureGenerator case where + // they are in a single blob. Hence QTextureImageData::data() is not suitable. + + uploadGLData(m_gl, + update.mipLevel(), update.layer(), + static_cast<QOpenGLTexture::CubeMapFace>(update.face()), + xOffset, yOffset, zOffset, + bytes, imgData); + } +} + +void GLTexture::updateGLTextureParameters() +{ + const QAbstractTexture::Target actualTarget = m_properties.target; + const bool isMultisampledTexture = (actualTarget == QAbstractTexture::Target2DMultisample || + actualTarget == QAbstractTexture::Target2DMultisampleArray); + // Multisampled textures can only be accessed by texelFetch in shaders + // and don't support wrap modes and mig/mag filtes + if (isMultisampledTexture) + return; + + m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX)); + if (actualTarget != QAbstractTexture::Target1D && + actualTarget != QAbstractTexture::Target1DArray && + actualTarget != QAbstractTexture::TargetBuffer) + m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY)); + if (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)); + } +} + +void GLTexture::introspectPropertiesFromSharedTextureId() +{ + // We know that the context is active when this function is called + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning() << Q_FUNC_INFO << "requires an OpenGL context"; + return; + } + QOpenGLFunctions *gl = ctx->functions(); + + // If the user has set the target format himself, we won't try to deduce it + if (m_properties.target != QAbstractTexture::TargetAutomatic) + return; + + const QAbstractTexture::Target targets[] = { + QAbstractTexture::Target2D, + QAbstractTexture::TargetCubeMap, +#ifndef QT_OPENGL_ES_2 + QAbstractTexture::Target1D, + QAbstractTexture::Target1DArray, + QAbstractTexture::Target3D, + QAbstractTexture::Target2DArray, + QAbstractTexture::TargetCubeMapArray, + QAbstractTexture::Target2DMultisample, + QAbstractTexture::Target2DMultisampleArray, + QAbstractTexture::TargetRectangle, + QAbstractTexture::TargetBuffer, +#endif + }; + +#ifndef QT_OPENGL_ES_2 + // Try to find texture target with GL 4.5 functions + const QPair<int, int> ctxGLVersion = ctx->format().version(); + if (ctxGLVersion.first > 4 || (ctxGLVersion.first == 4 && ctxGLVersion.second >= 5)) { + // Only for GL 4.5+ +#ifdef GL_TEXTURE_TARGET + QOpenGLFunctions_4_5_Core *gl5 = ctx->versionFunctions<QOpenGLFunctions_4_5_Core>(); + if (gl5 != nullptr) + gl5->glGetTextureParameteriv(m_sharedTextureId, GL_TEXTURE_TARGET, reinterpret_cast<int *>(&m_properties.target)); +#endif + } +#endif + + // If GL 4.5 function unavailable or not working, try a slower way + if (m_properties.target == QAbstractTexture::TargetAutomatic) { + // // OpenGL offers no proper way of querying for the target of a texture given its id + gl->glActiveTexture(GL_TEXTURE0); + + const GLenum targetBindings[] = { + GL_TEXTURE_BINDING_2D, + GL_TEXTURE_BINDING_CUBE_MAP, +#ifndef QT_OPENGL_ES_2 + GL_TEXTURE_BINDING_1D, + GL_TEXTURE_BINDING_1D_ARRAY, + GL_TEXTURE_BINDING_3D, + GL_TEXTURE_BINDING_2D_ARRAY, + GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, + GL_TEXTURE_BINDING_2D_MULTISAMPLE, + GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY, + GL_TEXTURE_BINDING_RECTANGLE, + GL_TEXTURE_BINDING_BUFFER +#endif + }; + + Q_ASSERT(sizeof(targetBindings) / sizeof(targetBindings[0] == sizeof(targets) / sizeof(targets[0]))); + + for (uint i = 0; i < sizeof(targetBindings) / sizeof(targetBindings[0]); ++i) { + const int target = targets[i]; + gl->glBindTexture(target, m_sharedTextureId); + int boundId = 0; + gl->glGetIntegerv(targetBindings[i], &boundId); + gl->glBindTexture(target, 0); + if (boundId == m_sharedTextureId) { + m_properties.target = static_cast<QAbstractTexture::Target>(target); + break; + } + } + } + + // Return early if we weren't able to find texture target + if (std::find(std::begin(targets), std::end(targets), m_properties.target) == std::end(targets)) { + qWarning() << "Unable to determine texture target for shared GL texture"; + return; + } + + // Bind texture once we know its target + gl->glBindTexture(m_properties.target, m_sharedTextureId); + + // TO DO: Improve by using glGetTextureParameters when available which + // support direct state access +#ifndef GL_TEXTURE_MAX_LEVEL +#define GL_TEXTURE_MAX_LEVEL 0x813D +#endif + +#ifndef GL_TEXTURE_WRAP_R +#define GL_TEXTURE_WRAP_R 0x8072 +#endif + + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAX_LEVEL, reinterpret_cast<int *>(&m_properties.mipLevels)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MIN_FILTER, reinterpret_cast<int *>(&m_parameters.minificationFilter)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAG_FILTER, reinterpret_cast<int *>(&m_parameters.magnificationFilter)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_R, reinterpret_cast<int *>(&m_parameters.wrapModeX)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_S, reinterpret_cast<int *>(&m_parameters.wrapModeY)); + gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_T, reinterpret_cast<int *>(&m_parameters.wrapModeZ)); + +#ifndef QT_OPENGL_ES_2 + // Try to retrieve dimensions (not available on ES 2.0) + if (!ctx->isOpenGLES()) { + QOpenGLFunctions_3_1 *gl3 = ctx->versionFunctions<QOpenGLFunctions_3_1>(); + if (!gl3) { + qWarning() << "Failed to retrieve shared texture dimensions"; + return; + } + + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_WIDTH, reinterpret_cast<int *>(&m_properties.width)); + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_HEIGHT, reinterpret_cast<int *>(&m_properties.height)); + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_DEPTH, reinterpret_cast<int *>(&m_properties.depth)); + gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_INTERNAL_FORMAT, reinterpret_cast<int *>(&m_properties.format)); + } +#endif + + gl->glBindTexture(m_properties.target, 0); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/renderers/opengl/textures/gltexture_p.h b/src/plugins/renderers/opengl/textures/gltexture_p.h new file mode 100644 index 000000000..ca851712d --- /dev/null +++ b/src/plugins/renderers/opengl/textures/gltexture_p.h @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** 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(); + ~GLTexture(); + + enum DirtyFlag { + None = 0, + TextureData = (1 << 0), // texture data needs uploading to GPU + Properties = (1 << 1), // texture needs to be (re-)created + Parameters = (1 << 2), // texture parameters need to be (re-)set + SharedTextureId = (1 << 3), // texture id from shared context + TextureImageData = (1 << 4) // texture image data needs uploading + }; + + /** + * Helper class to hold the defining properties of TextureImages + */ + struct Image { + QTextureImageDataGeneratorPtr generator; + int layer; + int mipLevel; + QAbstractTexture::CubeMapFace face; + + inline bool operator==(const Image &o) const { + bool sameGenerators = (generator == o.generator) + || (!generator.isNull() && !o.generator.isNull() && *generator == *o.generator); + return sameGenerators && layer == o.layer && mipLevel == o.mipLevel && face == o.face; + } + inline bool operator!=(const Image &o) const { return !(*this == o); } + }; + + inline TextureProperties properties() const { return m_properties; } + inline TextureParameters parameters() const { return m_parameters; } + inline QTextureGeneratorPtr textureGenerator() const { return m_dataFunctor; } + inline int sharedTextureId() const { return m_sharedTextureId; } + inline QVector<Image> images() const { return m_images; } + + inline QSize size() const { return QSize(m_properties.width, m_properties.height); } + inline 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. + */ + struct TextureUpdateInfo + { + QOpenGLTexture *texture = nullptr; + bool wasUpdated = false; + TextureProperties properties; + }; + + TextureUpdateInfo createOrUpdateGLTexture(); + + /** + * @brief + * Returns the RenderBuffer for this GLTexture. If this is the first + * call, the OpenGL renderbuffer object will be created. + */ + RenderBuffer *getOrCreateRenderBuffer(); + + + void destroy(); + + void cleanup(); + + bool isDirty() const + { + return m_dirtyFlags != None; + } + + bool hasTextureData() const { return !m_textureData.isNull(); } + bool hasImagesData() const { return !m_imageData.isEmpty(); } + + QFlags<DirtyFlag> dirtyFlags() const { return m_dirtyFlags; } + + QMutex *externalRenderingLock() + { + return &m_externalRenderingMutex; + } + + void setExternalRenderingEnabled(bool enable) + { + m_externalRendering = enable; + } + + bool isExternalRenderingEnabled() const + { + return m_externalRendering; + } + + // Purely for unit testing purposes + bool wasTextureRecreated() const + { + return m_wasTextureRecreated; + } + + void setParameters(const TextureParameters ¶ms); + void setProperties(const TextureProperties &props); + void setImages(const QVector<Image> &images); + void setGenerator(const QTextureGeneratorPtr &generator); + void setSharedTextureId(int textureId); + void addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates); + + QVector<QTextureDataUpdate> textureDataUpdates() const { return m_pendingTextureDataUpdates; } + QTextureGeneratorPtr dataGenerator() const { return m_dataFunctor; } + +private: + void requestImageUpload() + { + m_dirtyFlags |= TextureImageData; + } + + void requestUpload() + { + m_dirtyFlags |= TextureData; + } + + bool testDirtyFlag(DirtyFlag flag) + { + return m_dirtyFlags.testFlag(flag); + } + + void setDirtyFlag(DirtyFlag flag, bool value = true) + { + m_dirtyFlags.setFlag(flag, value); + } + + QOpenGLTexture *buildGLTexture(); + bool loadTextureDataFromGenerator(); + void loadTextureDataFromImages(); + void uploadGLTextureData(); + void updateGLTextureParameters(); + void introspectPropertiesFromSharedTextureId(); + void destroyResources(); + + QFlags<DirtyFlag> m_dirtyFlags; + QMutex m_externalRenderingMutex; + QOpenGLTexture *m_gl; + RenderBuffer *m_renderBuffer; + + // target which is actually used for GL texture + TextureProperties m_properties; + TextureParameters m_parameters; + + QTextureGeneratorPtr m_dataFunctor; + QTextureGenerator *m_pendingDataFunctor; + QVector<Image> m_images; + + // cache actual image data generated by the functors + QTextureDataPtr m_textureData; + QVector<QTextureImageDataPtr> m_imageData; + QVector<QTextureDataUpdate> m_pendingTextureDataUpdates; + + int m_sharedTextureId; + bool m_externalRendering; + bool m_wasTextureRecreated; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLTEXTURE_H diff --git a/src/plugins/renderers/opengl/textures/renderbuffer.cpp b/src/plugins/renderers/opengl/textures/renderbuffer.cpp new file mode 100644 index 000000000..bc5050f73 --- /dev/null +++ b/src/plugins/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/plugins/renderers/opengl/textures/renderbuffer_p.h b/src/plugins/renderers/opengl/textures/renderbuffer_p.h new file mode 100644 index 000000000..7dc62492a --- /dev/null +++ b/src/plugins/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/plugins/renderers/opengl/textures/textures.pri b/src/plugins/renderers/opengl/textures/textures.pri new file mode 100644 index 000000000..e17abfbb3 --- /dev/null +++ b/src/plugins/renderers/opengl/textures/textures.pri @@ -0,0 +1,9 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/gltexture.cpp \ + $$PWD/renderbuffer.cpp + +HEADERS += \ + $$PWD/gltexture_p.h \ + $$PWD/renderbuffer_p.h diff --git a/src/plugins/renderers/renderers.pro b/src/plugins/renderers/renderers.pro new file mode 100644 index 000000000..dc58bf7fc --- /dev/null +++ b/src/plugins/renderers/renderers.pro @@ -0,0 +1,9 @@ +TEMPLATE = subdirs + +# OpenGL renderer +include($$OUT_PWD/../../render/qt3drender-config.pri) +QT_FOR_CONFIG += 3drender-private + +#SUBDIRS += dummy + +qtConfig(qt3d-opengl-renderer): SUBDIRS += opengl |