diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2017-06-02 08:45:55 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2018-03-19 07:49:27 +0000 |
commit | 34f6d8a88677cffa44be05da7e1e2da0cfc2f3b4 (patch) | |
tree | d95b8632aa5a895b1eaa3cbb14891758923d93c9 /src/render/renderers | |
parent | e28192812168b676b57dc505b31eed3bfcba0e67 (diff) |
Move Renderer specific classes into new folder
This is another step toward isolating the renderer from the render aspect
Change-Id: I4031675b961d6645b65bbe05cf62d150993038b0
Task-number: QTBUG-61151
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'src/render/renderers')
66 files changed, 19883 insertions, 0 deletions
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp new file mode 100644 index 000000000..c2ec3db59 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp @@ -0,0 +1,975 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicscontext_p.h" + +#include <Qt3DRender/qgraphicsapifilter.h> +#include <Qt3DRender/qparameter.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/material_p.h> +#include <Qt3DRender/private/gltexture_p.h> +#include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/attribute_p.h> +#include <Qt3DRender/private/rendercommand_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/rendertarget_p.h> +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/gltexturemanager_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/qbuffer_p.h> +#include <Qt3DRender/private/renderbuffer_p.h> +#include <QOpenGLShaderProgram> + +#if !defined(QT_OPENGL_ES_2) +#include <QOpenGLFunctions_2_0> +#include <QOpenGLFunctions_3_2_Core> +#include <QOpenGLFunctions_3_3_Core> +#include <QOpenGLFunctions_4_3_Core> +#include <Qt3DRender/private/graphicshelpergl2_p.h> +#include <Qt3DRender/private/graphicshelpergl3_2_p.h> +#include <Qt3DRender/private/graphicshelpergl3_3_p.h> +#include <Qt3DRender/private/graphicshelpergl4_p.h> +#endif +#include <Qt3DRender/private/graphicshelperes2_p.h> +#include <Qt3DRender/private/graphicshelperes3_p.h> +#include <Qt3DRender/private/graphicshelperes3_2_p.h> + +#include <QSurface> +#include <QWindow> +#include <QOpenGLTexture> +#include <QOpenGLDebugLogger> + +QT_BEGIN_NAMESPACE + +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif + +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +namespace { + +QOpenGLShader::ShaderType shaderType(Qt3DRender::QShaderProgram::ShaderType type) +{ + switch (type) { + case Qt3DRender::QShaderProgram::Vertex: return QOpenGLShader::Vertex; + case Qt3DRender::QShaderProgram::TessellationControl: return QOpenGLShader::TessellationControl; + case Qt3DRender::QShaderProgram::TessellationEvaluation: return QOpenGLShader::TessellationEvaluation; + case Qt3DRender::QShaderProgram::Geometry: return QOpenGLShader::Geometry; + case Qt3DRender::QShaderProgram::Fragment: return QOpenGLShader::Fragment; + case Qt3DRender::QShaderProgram::Compute: return QOpenGLShader::Compute; + default: Q_UNREACHABLE(); + } +} + +} // anonymous namespace + +namespace Qt3DRender { +namespace Render { + +namespace { + +void logOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage) +{ + qDebug() << "OpenGL debug message:" << debugMessage; +} + +} // anonymous + +GraphicsContext::GraphicsContext() + : m_initialized(false) + , m_supportsVAO(false) + , m_maxTextureUnits(0) + , m_defaultFBO(0) + , m_gl(nullptr) + , m_glHelper(nullptr) + , m_shaderCache(nullptr) + , m_debugLogger(nullptr) +{ +} + +GraphicsContext::~GraphicsContext() +{ +} + +void GraphicsContext::setOpenGLContext(QOpenGLContext* ctx) +{ + Q_ASSERT(ctx); + m_gl = ctx; +} + +void GraphicsContext::initialize() +{ + m_initialized = true; + + Q_ASSERT(m_gl && m_shaderCache); + + m_gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &m_maxTextureUnits); + qCDebug(Backend) << "context supports" << m_maxTextureUnits << "texture units"; + + if (m_gl->format().majorVersion() >= 3) { + m_supportsVAO = true; + } else { + QSet<QByteArray> extensions = m_gl->extensions(); + m_supportsVAO = extensions.contains(QByteArrayLiteral("GL_OES_vertex_array_object")) + || extensions.contains(QByteArrayLiteral("GL_ARB_vertex_array_object")) + || extensions.contains(QByteArrayLiteral("GL_APPLE_vertex_array_object")); + } + + m_defaultFBO = m_gl->defaultFramebufferObject(); + qCDebug(Backend) << "VAO support = " << m_supportsVAO; +} + +void GraphicsContext::clearBackBuffer(QClearBuffers::BufferTypeFlags buffers) +{ + if (buffers != QClearBuffers::None) { + GLbitfield mask = 0; + + if (buffers & QClearBuffers::ColorBuffer) + mask |= GL_COLOR_BUFFER_BIT; + if (buffers & QClearBuffers::DepthBuffer) + mask |= GL_DEPTH_BUFFER_BIT; + if (buffers & QClearBuffers::StencilBuffer) + mask |= GL_STENCIL_BUFFER_BIT; + + m_gl->functions()->glClear(mask); + } +} + +bool GraphicsContext::hasValidGLHelper() const +{ + return m_glHelper != nullptr; +} + +bool GraphicsContext::isInitialized() const +{ + return m_initialized; +} + +bool GraphicsContext::makeCurrent(QSurface *surface) +{ + Q_ASSERT(m_gl); + if (!m_gl->makeCurrent(surface)) { + qCWarning(Backend) << Q_FUNC_INFO << "makeCurrent failed"; + return false; + } + + // Set the correct GL Helper depending on the surface + // If no helper exists, create one + + m_glHelper = m_glHelpers.value(surface); + if (!m_glHelper) { + m_glHelper = resolveHighestOpenGLFunctions(); + m_glHelpers.insert(surface, m_glHelper); + } + return true; +} + +void GraphicsContext::doneCurrent() +{ + Q_ASSERT(m_gl); + m_gl->doneCurrent(); + m_glHelper = nullptr; +} + +// Called by GL Command Thread +QOpenGLShaderProgram *GraphicsContext::createShaderProgram(Shader *shaderNode) +{ + QScopedPointer<QOpenGLShaderProgram> shaderProgram(new QOpenGLShaderProgram); + + // Compile shaders + const auto shaderCode = shaderNode->shaderCode(); + QString logs; + for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) { + QShaderProgram::ShaderType type = static_cast<const QShaderProgram::ShaderType>(i); + if (!shaderCode.at(i).isEmpty()) { + // Note: logs only return the error but not all the shader code + // we could append it + if (!shaderProgram->addCacheableShaderFromSourceCode(shaderType(type), shaderCode.at(i))) + logs += shaderProgram->log(); + } + } + + // Call glBindFragDataLocation and link the program + // Since we are sharing shaders in the backend, we assume that if using custom + // fragOutputs, they should all be the same for a given shader + bindFragOutputs(shaderProgram->programId(), shaderNode->fragOutputs()); + + const bool linkSucceeded = shaderProgram->link(); + logs += shaderProgram->log(); + shaderNode->setLog(logs); + shaderNode->setStatus(linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error); + + if (!linkSucceeded) + return nullptr; + + // take from scoped-pointer so it doesn't get deleted + return shaderProgram.take(); +} + +// Called by GL Command Thread (can't use global glHelpers) +// That assumes that the shaderProgram in Shader stays the same +void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram) +{ + GraphicsHelperInterface *glHelper = resolveHighestOpenGLFunctions(); + shader->initializeUniforms(glHelper->programUniformsAndLocations(shaderProgram->programId())); + shader->initializeAttributes(glHelper->programAttributesAndLocations(shaderProgram->programId())); + if (m_glHelper->supportsFeature(GraphicsHelperInterface::UniformBufferObject)) + shader->initializeUniformBlocks(glHelper->programUniformBlocks(shaderProgram->programId())); + if (m_glHelper->supportsFeature(GraphicsHelperInterface::ShaderStorageObject)) + shader->initializeShaderStorageBlocks(glHelper->programShaderStorageBlocks(shaderProgram->programId())); +} + + +// Called by GL Command Thread +void GraphicsContext::loadShader(Shader *shader, ShaderManager *manager) +{ + bool wasPresent = false; + QOpenGLShaderProgram *shaderProgram = m_shaderCache->getShaderProgramAndAddRef(shader->dna(), shader->peerId(), &wasPresent); + if (!shaderProgram && !wasPresent) { + // No matching QOpenGLShader in the cache so create one + shaderProgram = createShaderProgram(shader); + + // Store in cache (even when failed and shaderProgram is null) + m_shaderCache->insert(shader->dna(), shader->peerId(), shaderProgram); + } + + // Ensure the Shader node knows about the program interface + if (Q_LIKELY(shaderProgram != nullptr) && !shader->isLoaded()) { + + // Find an already loaded shader that shares the same QOpenGLShaderProgram + Shader *refShader = nullptr; + const QVector<Qt3DCore::QNodeId> sharedShaderIds = m_shaderCache->shaderIdsForProgram(shader->dna()); + for (const Qt3DCore::QNodeId sharedShaderId : sharedShaderIds) { + Shader *sharedShader = manager->lookupResource(sharedShaderId); + // Note: no need to check if shader->peerId != sharedShader->peerId + // as we are sure that this code path is executed only if !shared->isLoaded + if (sharedShader->isLoaded()) { + refShader = sharedShader; + break; + } + } + + // We only introspect once per actual OpenGL shader program + // rather than once per ShaderNode. + if (refShader != nullptr) + shader->initializeFromReference(*refShader); + else // Introspect and set up interface description on Shader backend node + introspectShaderInterface(shader, shaderProgram); + + shader->setGraphicsContext(this); + shader->setLoaded(true); + shader->markDirty(AbstractRenderer::AllDirty); + } +} + +void GraphicsContext::removeShaderProgramReference(Shader *shaderNode) +{ + m_shaderCache->removeRef(shaderNode->dna(), shaderNode->peerId()); +} + +void GraphicsContext::activateDrawBuffers(const AttachmentPack &attachments) +{ + const QVector<int> activeDrawBuffers = attachments.getGlDrawBuffers(); + + if (m_glHelper->checkFrameBufferComplete()) { + if (activeDrawBuffers.size() > 1) {// We need MRT + if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) { + // Set up MRT, glDrawBuffers... + m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data()); + } + } + } else { + qWarning() << "FBO incomplete"; + } +} + +/*! + * \internal + * Finds the highest supported opengl version and internally use the most optimized + * helper for a given version. + */ +GraphicsHelperInterface *GraphicsContext::resolveHighestOpenGLFunctions() +{ + Q_ASSERT(m_gl); + GraphicsHelperInterface *glHelper = nullptr; + + if (m_gl->isOpenGLES()) { + if (m_gl->format().majorVersion() >= 3) { + if (m_gl->format().minorVersion() >= 2) { + glHelper = new GraphicsHelperES3_2; + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL ES 3.2 Helper"; + } else { + glHelper = new GraphicsHelperES3(); + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL ES 3.0 Helper"; + } + } else { + glHelper = new GraphicsHelperES2(); + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL ES2 Helper"; + } + glHelper->initializeHelper(m_gl, nullptr); + } +#ifndef QT_OPENGL_ES_2 + else { + QAbstractOpenGLFunctions *glFunctions = nullptr; + if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_4_3_Core>()) != nullptr) { + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 4.3"; + glHelper = new GraphicsHelperGL4(); + } else if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_3_3_Core>()) != nullptr) { + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 3.3"; + glHelper = new GraphicsHelperGL3_3(); + } else if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_3_2_Core>()) != nullptr) { + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 3.2"; + glHelper = new GraphicsHelperGL3_2(); + } else if ((glFunctions = m_gl->versionFunctions<QOpenGLFunctions_2_0>()) != nullptr) { + qCDebug(Backend) << Q_FUNC_INFO << " Building OpenGL 2 Helper"; + glHelper = new GraphicsHelperGL2(); + } + Q_ASSERT_X(glHelper, "GraphicsContext::resolveHighestOpenGLFunctions", "unable to create valid helper for available OpenGL version"); + glHelper->initializeHelper(m_gl, glFunctions); + } +#endif + + // Note: at this point we are certain the context (m_gl) is current with a surface + const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING"); + const bool enableDebugLogging = !debugLoggingMode.isEmpty(); + + if (enableDebugLogging && !m_debugLogger) { + if (m_gl->hasExtension("GL_KHR_debug")) { + qCDebug(Backend) << "Qt3D: Enabling OpenGL debug logging"; + m_debugLogger.reset(new QOpenGLDebugLogger); + if (m_debugLogger->initialize()) { + QObject::connect(m_debugLogger.data(), &QOpenGLDebugLogger::messageLogged, &logOpenGLDebugMessage); + const QString mode = QString::fromLocal8Bit(debugLoggingMode); + m_debugLogger->startLogging(mode.startsWith(QLatin1String("sync"), Qt::CaseInsensitive) + ? QOpenGLDebugLogger::SynchronousLogging + : QOpenGLDebugLogger::AsynchronousLogging); + + const auto msgs = m_debugLogger->loggedMessages(); + for (const QOpenGLDebugMessage &msg : msgs) + logOpenGLDebugMessage(msg); + } + } else { + qCDebug(Backend) << "Qt3D: OpenGL debug logging requested but GL_KHR_debug not supported"; + } + } + + + // Set Vendor and Extensions of reference GraphicsApiFilter + // TO DO: would that vary like the glHelper ? + + QStringList extensions; + const auto exts = m_gl->extensions(); + for (const QByteArray &ext : exts) + extensions << QString::fromUtf8(ext); + m_contextInfo.m_major = m_gl->format().version().first; + m_contextInfo.m_minor = m_gl->format().version().second; + m_contextInfo.m_api = m_gl->isOpenGLES() ? QGraphicsApiFilter::OpenGLES : QGraphicsApiFilter::OpenGL; + m_contextInfo.m_profile = static_cast<QGraphicsApiFilter::OpenGLProfile>(m_gl->format().profile()); + m_contextInfo.m_extensions = extensions; + m_contextInfo.m_vendor = QString::fromUtf8(reinterpret_cast<const char *>(m_gl->functions()->glGetString(GL_VENDOR))); + + return glHelper; +} + +const GraphicsApiFilterData *GraphicsContext::contextInfo() const +{ + return &m_contextInfo; +} + +bool GraphicsContext::supportsDrawBuffersBlend() const +{ + return m_glHelper->supportsFeature(GraphicsHelperInterface::DrawBuffersBlend); +} + +/*! + * \internal + * Wraps an OpenGL call to glDrawElementsInstanced. + * If the call is not supported by the system's OpenGL version, + * it is simulated with a loop. + */ +void GraphicsContext::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLsizei instances, + GLint baseVertex, + GLint baseInstance) +{ + m_glHelper->drawElementsInstancedBaseVertexBaseInstance(primitiveType, + primitiveCount, + indexType, + indices, + instances, + baseVertex, + baseInstance); +} + +/*! + * \internal + * Wraps an OpenGL call to glDrawArraysInstanced. + */ +void GraphicsContext::drawArraysInstanced(GLenum primitiveType, + GLint first, + GLsizei count, + GLsizei instances) +{ + m_glHelper->drawArraysInstanced(primitiveType, + first, + count, + instances); +} + +void GraphicsContext::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseinstance) +{ + m_glHelper->drawArraysInstancedBaseInstance(primitiveType, + first, + count, + instances, + baseinstance); +} + +/*! + * \internal + * Wraps an OpenGL call to glDrawElements. + */ +void GraphicsContext::drawElements(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLint baseVertex) +{ + m_glHelper->drawElements(primitiveType, + primitiveCount, + indexType, + indices, + baseVertex); +} + +void GraphicsContext::drawElementsIndirect(GLenum mode, + GLenum type, + void *indirect) +{ + m_glHelper->drawElementsIndirect(mode, type, indirect); +} + +/*! + * \internal + * Wraps an OpenGL call to glDrawArrays. + */ +void GraphicsContext::drawArrays(GLenum primitiveType, + GLint first, + GLsizei count) +{ + m_glHelper->drawArrays(primitiveType, + first, + count); +} + +void GraphicsContext::drawArraysIndirect(GLenum mode, void *indirect) +{ + m_glHelper->drawArraysIndirect(mode, indirect); +} + +void GraphicsContext::setVerticesPerPatch(GLint verticesPerPatch) +{ + m_glHelper->setVerticesPerPatch(verticesPerPatch); +} + +void GraphicsContext::blendEquation(GLenum mode) +{ + m_glHelper->blendEquation(mode); +} + +void GraphicsContext::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) +{ + m_glHelper->blendFunci(buf, sfactor, dfactor); +} + +void GraphicsContext::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) +{ + m_glHelper->blendFuncSeparatei(buf, sRGB, dRGB, sAlpha, dAlpha); +} + +void GraphicsContext::alphaTest(GLenum mode1, GLenum mode2) +{ + m_glHelper->alphaTest(mode1, mode2); +} + +void GraphicsContext::bindFramebuffer(GLuint fbo, GraphicsHelperInterface::FBOBindMode mode) +{ + m_glHelper->bindFrameBufferObject(fbo, mode); +} + +void GraphicsContext::depthTest(GLenum mode) +{ + m_glHelper->depthTest(mode); +} + +void GraphicsContext::depthMask(GLenum mode) +{ + m_glHelper->depthMask(mode); +} + +void GraphicsContext::frontFace(GLenum mode) +{ + m_glHelper->frontFace(mode); +} + +void GraphicsContext::bindFragOutputs(GLuint shader, const QHash<QString, int> &outputs) +{ + if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT) && + m_glHelper->supportsFeature(GraphicsHelperInterface::BindableFragmentOutputs)) + m_glHelper->bindFragDataLocation(shader, outputs); +} + +void GraphicsContext::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +{ + m_glHelper->bindUniformBlock(programId, uniformBlockIndex, uniformBlockBinding); +} + +void GraphicsContext::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) +{ + m_glHelper->bindShaderStorageBlock(programId, shaderStorageBlockIndex, shaderStorageBlockBinding); +} + +void GraphicsContext::bindBufferBase(GLenum target, GLuint bindingIndex, GLuint buffer) +{ + m_glHelper->bindBufferBase(target, bindingIndex, buffer); +} + +void GraphicsContext::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + m_glHelper->buildUniformBuffer(v, description, buffer); +} + +void GraphicsContext::setMSAAEnabled(bool enabled) +{ + m_glHelper->setMSAAEnabled(enabled); +} + +void GraphicsContext::setAlphaCoverageEnabled(bool enabled) +{ + m_glHelper->setAlphaCoverageEnabled(enabled); +} + +void GraphicsContext::clearBufferf(GLint drawbuffer, const QVector4D &values) +{ + m_glHelper->clearBufferf(drawbuffer, values); +} + +GLuint GraphicsContext::boundFrameBufferObject() +{ + return m_glHelper->boundFrameBufferObject(); +} + +void GraphicsContext::clearColor(const QColor &color) +{ + m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); +} + +void GraphicsContext::clearDepthValue(float depth) +{ + m_gl->functions()->glClearDepthf(depth); +} + +void GraphicsContext::clearStencilValue(int stencil) +{ + m_gl->functions()->glClearStencil(stencil); +} + +void GraphicsContext::enableClipPlane(int clipPlane) +{ + m_glHelper->enableClipPlane(clipPlane); +} + +void GraphicsContext::disableClipPlane(int clipPlane) +{ + m_glHelper->disableClipPlane(clipPlane); +} + +void GraphicsContext::setClipPlane(int clipPlane, const QVector3D &normal, float distance) +{ + m_glHelper->setClipPlane(clipPlane, normal, distance); +} + +GLint GraphicsContext::maxClipPlaneCount() +{ + return m_glHelper->maxClipPlaneCount(); +} + +GLint GraphicsContext::maxTextureUnitsCount() +{ + return m_maxTextureUnits; +} + +void GraphicsContext::enablePrimitiveRestart(int restartIndex) +{ + if (m_glHelper->supportsFeature(GraphicsHelperInterface::PrimitiveRestart)) + m_glHelper->enablePrimitiveRestart(restartIndex); +} + +void GraphicsContext::disablePrimitiveRestart() +{ + if (m_glHelper->supportsFeature(GraphicsHelperInterface::PrimitiveRestart)) + m_glHelper->disablePrimitiveRestart(); +} + +void GraphicsContext::pointSize(bool programmable, GLfloat value) +{ + m_glHelper->pointSize(programmable, value); +} + +void GraphicsContext::dispatchCompute(int x, int y, int z) +{ + if (m_glHelper->supportsFeature(GraphicsHelperInterface::Compute)) + m_glHelper->dispatchCompute(x, y, z); +} + +GLboolean GraphicsContext::unmapBuffer(GLenum target) +{ + return m_glHelper->unmapBuffer(target); +} + +char *GraphicsContext::mapBuffer(GLenum target, GLsizeiptr size) +{ + return m_glHelper->mapBuffer(target, size); +} + +void GraphicsContext::enablei(GLenum cap, GLuint index) +{ + m_glHelper->enablei(cap, index); +} + +void GraphicsContext::disablei(GLenum cap, GLuint index) +{ + m_glHelper->disablei(cap, index); +} + +void GraphicsContext::setSeamlessCubemap(bool enable) +{ + m_glHelper->setSeamlessCubemap(enable); +} + +void GraphicsContext::readBuffer(GLenum mode) +{ + m_glHelper->readBuffer(mode); +} + +void GraphicsContext::drawBuffer(GLenum mode) +{ + m_glHelper->drawBuffer(mode); +} + +void GraphicsContext::applyUniform(const ShaderUniform &description, const UniformValue &v) +{ + const UniformType type = m_glHelper->uniformTypeFromGLType(description.m_type); + + switch (type) { + case UniformType::Float: + // See QTBUG-57510 and uniform_p.h + if (v.storedType() == Int) { + float value = float(*v.constData<int>()); + UniformValue floatV(value); + applyUniformHelper<UniformType::Float>(description, floatV); + } else { + applyUniformHelper<UniformType::Float>(description, v); + } + break; + case UniformType::Vec2: + applyUniformHelper<UniformType::Vec2>(description, v); + break; + case UniformType::Vec3: + applyUniformHelper<UniformType::Vec3>(description, v); + break; + case UniformType::Vec4: + applyUniformHelper<UniformType::Vec4>(description, v); + break; + + case UniformType::Double: + applyUniformHelper<UniformType::Double>(description, v); + break; + case UniformType::DVec2: + applyUniformHelper<UniformType::DVec2>(description, v); + break; + case UniformType::DVec3: + applyUniformHelper<UniformType::DVec3>(description, v); + break; + case UniformType::DVec4: + applyUniformHelper<UniformType::DVec4>(description, v); + break; + + case UniformType::Sampler: + case UniformType::Int: + applyUniformHelper<UniformType::Int>(description, v); + break; + case UniformType::IVec2: + applyUniformHelper<UniformType::IVec2>(description, v); + break; + case UniformType::IVec3: + applyUniformHelper<UniformType::IVec3>(description, v); + break; + case UniformType::IVec4: + applyUniformHelper<UniformType::IVec4>(description, v); + break; + + case UniformType::UInt: + applyUniformHelper<UniformType::UInt>(description, v); + break; + case UniformType::UIVec2: + applyUniformHelper<UniformType::UIVec2>(description, v); + break; + case UniformType::UIVec3: + applyUniformHelper<UniformType::UIVec3>(description, v); + break; + case UniformType::UIVec4: + applyUniformHelper<UniformType::UIVec4>(description, v); + break; + + case UniformType::Bool: + applyUniformHelper<UniformType::Bool>(description, v); + break; + case UniformType::BVec2: + applyUniformHelper<UniformType::BVec2>(description, v); + break; + case UniformType::BVec3: + applyUniformHelper<UniformType::BVec3>(description, v); + break; + case UniformType::BVec4: + applyUniformHelper<UniformType::BVec4>(description, v); + break; + + case UniformType::Mat2: + applyUniformHelper<UniformType::Mat2>(description, v); + break; + case UniformType::Mat3: + applyUniformHelper<UniformType::Mat3>(description, v); + break; + case UniformType::Mat4: + applyUniformHelper<UniformType::Mat4>(description, v); + break; + case UniformType::Mat2x3: + applyUniformHelper<UniformType::Mat2x3>(description, v); + break; + case UniformType::Mat3x2: + applyUniformHelper<UniformType::Mat3x2>(description, v); + break; + case UniformType::Mat2x4: + applyUniformHelper<UniformType::Mat2x4>(description, v); + break; + case UniformType::Mat4x2: + applyUniformHelper<UniformType::Mat4x2>(description, v); + break; + case UniformType::Mat3x4: + applyUniformHelper<UniformType::Mat3x4>(description, v); + break; + case UniformType::Mat4x3: + applyUniformHelper<UniformType::Mat4x3>(description, v); + break; + + default: + break; + } +} + +void GraphicsContext::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + m_glHelper->memoryBarrier(barriers); +} + +GLint GraphicsContext::elementType(GLint type) +{ + switch (type) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + return GL_FLOAT; + +#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2 + case GL_DOUBLE: +#ifdef GL_DOUBLE_VEC3 // For compiling on pre GL 4.1 systems + case GL_DOUBLE_VEC2: + case GL_DOUBLE_VEC3: + case GL_DOUBLE_VEC4: +#endif + return GL_DOUBLE; +#endif + default: + qWarning() << Q_FUNC_INFO << "unsupported:" << QString::number(type, 16); + } + + return GL_INVALID_VALUE; +} + +GLint GraphicsContext::tupleSizeFromType(GLint type) +{ + switch (type) { + case GL_FLOAT: +#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2 + case GL_DOUBLE: +#endif + case GL_UNSIGNED_BYTE: + case GL_UNSIGNED_INT: + break; // fall through + + case GL_FLOAT_VEC2: +#ifdef GL_DOUBLE_VEC2 // For compiling on pre GL 4.1 systems. + case GL_DOUBLE_VEC2: +#endif + return 2; + + case GL_FLOAT_VEC3: +#ifdef GL_DOUBLE_VEC3 // For compiling on pre GL 4.1 systems. + case GL_DOUBLE_VEC3: +#endif + return 3; + + case GL_FLOAT_VEC4: +#ifdef GL_DOUBLE_VEC4 // For compiling on pre GL 4.1 systems. + case GL_DOUBLE_VEC4: +#endif + return 4; + + default: + qWarning() << Q_FUNC_INFO << "unsupported:" << QString::number(type, 16); + } + + return 1; +} + +GLuint GraphicsContext::byteSizeFromType(GLint type) +{ + switch (type) { + case GL_FLOAT: return sizeof(float); +#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2 + case GL_DOUBLE: return sizeof(double); +#endif + case GL_UNSIGNED_BYTE: return sizeof(unsigned char); + case GL_UNSIGNED_INT: return sizeof(GLuint); + + case GL_FLOAT_VEC2: return sizeof(float) * 2; + case GL_FLOAT_VEC3: return sizeof(float) * 3; + case GL_FLOAT_VEC4: return sizeof(float) * 4; +#ifdef GL_DOUBLE_VEC3 // Required to compile on pre GL 4.1 systems + case GL_DOUBLE_VEC2: return sizeof(double) * 2; + case GL_DOUBLE_VEC3: return sizeof(double) * 3; + case GL_DOUBLE_VEC4: return sizeof(double) * 4; +#endif + default: + qWarning() << Q_FUNC_INFO << "unsupported:" << QString::number(type, 16); + } + + return 0; +} + +GLint GraphicsContext::glDataTypeFromAttributeDataType(QAttribute::VertexBaseType dataType) +{ + switch (dataType) { + case QAttribute::Byte: + return GL_BYTE; + case QAttribute::UnsignedByte: + return GL_UNSIGNED_BYTE; + case QAttribute::Short: + return GL_SHORT; + case QAttribute::UnsignedShort: + return GL_UNSIGNED_SHORT; + case QAttribute::Int: + return GL_INT; + case QAttribute::UnsignedInt: + return GL_UNSIGNED_INT; + case QAttribute::HalfFloat: +#ifdef GL_HALF_FLOAT + return GL_HALF_FLOAT; +#endif +#ifndef QT_OPENGL_ES_2 // Otherwise compile error as Qt defines GL_DOUBLE as GL_FLOAT when using ES2 + case QAttribute::Double: + return GL_DOUBLE; +#endif + case QAttribute::Float: + break; + default: + qWarning() << Q_FUNC_INFO << "unsupported dataType:" << dataType; + } + return GL_FLOAT; +} + +QT3D_UNIFORM_TYPE_IMPL(UniformType::Float, float, glUniform1fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec2, float, glUniform2fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec3, float, glUniform3fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Vec4, float, glUniform4fv) + +// OpenGL expects int* as values for booleans +QT3D_UNIFORM_TYPE_IMPL(UniformType::Bool, int, glUniform1iv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::BVec2, int, glUniform2iv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::BVec3, int, glUniform3iv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::BVec4, int, glUniform4iv) + +QT3D_UNIFORM_TYPE_IMPL(UniformType::Int, int, glUniform1iv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::IVec2, int, glUniform2iv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::IVec3, int, glUniform3iv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::IVec4, int, glUniform4iv) + +QT3D_UNIFORM_TYPE_IMPL(UniformType::UInt, uint, glUniform1uiv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::UIVec2, uint, glUniform2uiv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::UIVec3, uint, glUniform3uiv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::UIVec4, uint, glUniform4uiv) + +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat2, float, glUniformMatrix2fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat3, float, glUniformMatrix3fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat4, float, glUniformMatrix4fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat2x3, float, glUniformMatrix2x3fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat3x2, float, glUniformMatrix3x2fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat2x4, float, glUniformMatrix2x4fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat4x2, float, glUniformMatrix4x2fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat3x4, float, glUniformMatrix3x4fv) +QT3D_UNIFORM_TYPE_IMPL(UniformType::Mat4x3, float, glUniformMatrix4x3fv) + +} // namespace Render +} // namespace Qt3DRender of namespace + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h new file mode 100644 index 000000000..82db57433 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSCONTEXT_H +#define QT3DRENDER_RENDER_GRAPHICSCONTEXT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QOpenGLContext> +#include <QOpenGLFunctions> +#include <QOpenGLVertexArrayObject> +#include <QHash> +#include <QColor> +#include <QMatrix4x4> +#include <QBitArray> +#include <QImage> +#include <Qt3DRender/private/shaderparameterpack_p.h> +#include <Qt3DRender/qclearbuffers.h> +#include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/glbuffer_p.h> +#include <Qt3DRender/qattribute.h> +#include <Qt3DRender/qmemorybarrier.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/qgraphicsapifilter_p.h> +#include <Qt3DRender/private/shadercache_p.h> +#include <Qt3DRender/private/uniform_p.h> +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <Qt3DRender/private/qblitframebuffer_p.h> +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; +class QAbstractOpenGLFunctions; +class QOpenGLDebugLogger; + +namespace Qt3DRender { + +namespace Render { + +class GraphicsHelperInterface; +class RenderTarget; +class AttachmentPack; +class ShaderManager; + +typedef QPair<QString, int> NamedUniformLocation; + +class Q_AUTOTEST_EXPORT GraphicsContext +{ +public: + GraphicsContext(); + ~GraphicsContext(); + + void setShaderCache(ShaderCache *shaderCache) { m_shaderCache = shaderCache; } + ShaderCache *shaderCache() const { return m_shaderCache; } + + void setOpenGLContext(QOpenGLContext* ctx); + QOpenGLContext *openGLContext() { return m_gl; } + bool makeCurrent(QSurface *surface); + void doneCurrent(); + bool hasValidGLHelper() const; + bool isInitialized() const; + + // Shaders + QOpenGLShaderProgram *createShaderProgram(Shader *shaderNode); + void introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram); + void loadShader(Shader* shader, ShaderManager *manager); + void removeShaderProgramReference(Shader *shaderNode); + + GLuint defaultFBO() const { return m_defaultFBO; } + + const GraphicsApiFilterData *contextInfo() const; + + // Wrapper methods + void clearBackBuffer(QClearBuffers::BufferTypeFlags buffers); + void alphaTest(GLenum mode1, GLenum mode2); + void bindFramebuffer(GLuint fbo, GraphicsHelperInterface::FBOBindMode mode); + void bindBufferBase(GLenum target, GLuint bindingIndex, GLuint buffer); + void bindFragOutputs(GLuint shader, const QHash<QString, int> &outputs); + void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding); + void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding); + void blendEquation(GLenum mode); + void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor); + void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha); + GLuint boundFrameBufferObject(); + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer); + void clearBufferf(GLint drawbuffer, const QVector4D &values); + void clearColor(const QColor &color); + void clearDepthValue(float depth); + void clearStencilValue(int stencil); + void depthMask(GLenum mode); + void depthTest(GLenum mode); + void disableClipPlane(int clipPlane); + void disablei(GLenum cap, GLuint index); + void disablePrimitiveRestart(); + void dispatchCompute(int x, int y, int z); + char * mapBuffer(GLenum target, GLsizeiptr size); + GLboolean unmapBuffer(GLenum target); + void drawArrays(GLenum primitiveType, GLint first, GLsizei count); + void drawArraysIndirect(GLenum mode,void *indirect); + void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances); + void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseinstance); + void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLint baseVertex); + void drawElementsIndirect(GLenum mode, GLenum type, void *indirect); + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLsizei instances, GLint baseVertex, GLint baseInstance); + void enableClipPlane(int clipPlane); + void enablei(GLenum cap, GLuint index); + void enablePrimitiveRestart(int restartIndex); + void frontFace(GLenum mode); + GLint maxClipPlaneCount(); + GLint maxTextureUnitsCount(); + void pointSize(bool programmable, GLfloat value); + void readBuffer(GLenum mode); + void drawBuffer(GLenum mode); + void setMSAAEnabled(bool enabled); + void setAlphaCoverageEnabled(bool enabled); + void setClipPlane(int clipPlane, const QVector3D &normal, float distance); + void setSeamlessCubemap(bool enable); + void setVerticesPerPatch(GLint verticesPerPatch); + void memoryBarrier(QMemoryBarrier::Operations barriers); + void activateDrawBuffers(const AttachmentPack &attachments); + + // Helper methods + static GLint elementType(GLint type); + static GLint tupleSizeFromType(GLint type); + static GLuint byteSizeFromType(GLint type); + static GLint glDataTypeFromAttributeDataType(QAttribute::VertexBaseType dataType); + + bool supportsDrawBuffersBlend() const; + bool supportsVAO() const { return m_supportsVAO; } + + void initialize(); + GraphicsHelperInterface *resolveHighestOpenGLFunctions(); + + bool m_initialized; + bool m_supportsVAO; + GLint m_maxTextureUnits; + GLuint m_defaultFBO; + QOpenGLContext *m_gl; + GraphicsHelperInterface *m_glHelper; + + QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers; + GraphicsApiFilterData m_contextInfo; + ShaderCache *m_shaderCache; + QScopedPointer<QOpenGLDebugLogger> m_debugLogger; + + friend class OpenGLVertexArrayObject; + OpenGLVertexArrayObject *m_currentVAO; + + void applyUniform(const ShaderUniform &description, const UniformValue &v); + + template<UniformType> + void applyUniformHelper(const ShaderUniform &, const UniformValue &) const + { + Q_ASSERT_X(false, Q_FUNC_INFO, "Uniform: Didn't provide specialized apply() implementation"); + } +}; + +#define QT3D_UNIFORM_TYPE_PROTO(UniformTypeEnum, BaseType, Func) \ +template<> \ +void GraphicsContext::applyUniformHelper<UniformTypeEnum>(const ShaderUniform &description, const UniformValue &value) const; + +#define QT3D_UNIFORM_TYPE_IMPL(UniformTypeEnum, BaseType, Func) \ + template<> \ + void GraphicsContext::applyUniformHelper<UniformTypeEnum>(const ShaderUniform &description, const UniformValue &value) const \ +{ \ + const int count = qMin(description.m_size, int(value.byteSize() / description.m_rawByteSize)); \ + m_glHelper->Func(description.m_location, count, value.constData<BaseType>()); \ +} + + +QT3D_UNIFORM_TYPE_PROTO(UniformType::Float, float, glUniform1fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Vec2, float, glUniform2fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Vec3, float, glUniform3fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Vec4, float, glUniform4fv) + +// OpenGL expects int* as values for booleans +QT3D_UNIFORM_TYPE_PROTO(UniformType::Bool, int, glUniform1iv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::BVec2, int, glUniform2iv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::BVec3, int, glUniform3iv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::BVec4, int, glUniform4iv) + +QT3D_UNIFORM_TYPE_PROTO(UniformType::Int, int, glUniform1iv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::IVec2, int, glUniform2iv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::IVec3, int, glUniform3iv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::IVec4, int, glUniform4iv) + +QT3D_UNIFORM_TYPE_PROTO(UniformType::UInt, uint, glUniform1uiv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::UIVec2, uint, glUniform2uiv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::UIVec3, uint, glUniform3uiv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::UIVec4, uint, glUniform4uiv) + +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat2, float, glUniformMatrix2fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat3, float, glUniformMatrix3fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat4, float, glUniformMatrix4fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat2x3, float, glUniformMatrix2x3fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat3x2, float, glUniformMatrix3x2fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat2x4, float, glUniformMatrix2x4fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat4x2, float, glUniformMatrix4x2fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat3x4, float, glUniformMatrix3x4fv) +QT3D_UNIFORM_TYPE_PROTO(UniformType::Mat4x3, float, glUniformMatrix4x3fv) + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSCONTEXT_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp new file mode 100644 index 000000000..966528ad8 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp @@ -0,0 +1,846 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicshelperes2_p.h" +#include <Qt3DRender/private/renderlogging_p.h> +#include <private/attachmentpack_p.h> +#include <private/qgraphicsutils_p.h> +#include <private/renderbuffer_p.h> +#include <QtGui/private/qopenglextensions_p.h> + +QT_BEGIN_NAMESPACE + +// ES 3.0+ +#ifndef GL_SAMPLER_3D +#define GL_SAMPLER_3D 0x8B5F +#endif +#ifndef GL_SAMPLER_2D_SHADOW +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#endif +#ifndef GL_SAMPLER_CUBE_SHADOW +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#endif +#ifndef GL_SAMPLER_2D_ARRAY +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#endif +#ifndef GL_SAMPLER_2D_ARRAY_SHADOW +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#endif + +// ES 2.0 FBO +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif + +namespace Qt3DRender { +namespace Render { + +GraphicsHelperES2::GraphicsHelperES2() + : m_funcs(0) + , m_supportFramebufferBlit(false) +{ +} + +GraphicsHelperES2::~GraphicsHelperES2() +{ +} + +void GraphicsHelperES2::initializeHelper(QOpenGLContext *context, + QAbstractOpenGLFunctions *) +{ + Q_ASSERT(context); + m_funcs = context->functions(); + Q_ASSERT(m_funcs); + m_ext.reset(new QOpenGLExtensions(context)); + if (m_ext->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) + m_supportFramebufferBlit = true; +} + +void GraphicsHelperES2::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLsizei instances, + GLint baseVertex, + GLint baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2"; + + if (baseVertex != 0) + qWarning() << "glDrawElementsInstancedBaseVertex is not supported with OpenGL ES 2"; + + for (GLint i = 0; i < instances; i++) + drawElements(primitiveType, + primitiveCount, + indexType, + indices); +} + +void GraphicsHelperES2::drawArraysInstanced(GLenum primitiveType, + GLint first, + GLsizei count, + GLsizei instances) +{ + for (GLint i = 0; i < instances; i++) + drawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperES2::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL ES 2"; + for (GLint i = 0; i < instances; i++) + drawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperES2::drawElements(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLint baseVertex) +{ + if (baseVertex != 0) + qWarning() << "glDrawElementsBaseVertex is not supported with OpenGL ES 2"; + QOpenGLExtensions *xfuncs = static_cast<QOpenGLExtensions *>(m_funcs); + if (indexType == GL_UNSIGNED_INT && !xfuncs->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) { + static bool warnShown = false; + if (!warnShown) { + warnShown = true; + qWarning("GL_UNSIGNED_INT index type not supported on this system, skipping draw call."); + } + return; + } + m_funcs->glDrawElements(primitiveType, + primitiveCount, + indexType, + indices); +} + +void GraphicsHelperES2::drawArrays(GLenum primitiveType, + GLint first, + GLsizei count) +{ + m_funcs->glDrawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperES2::drawElementsIndirect(GLenum, GLenum, void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL ES 2"; +} + +void GraphicsHelperES2::drawArraysIndirect(GLenum , void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL ES 2"; +} + +void GraphicsHelperES2::setVerticesPerPatch(GLint verticesPerPatch) +{ + Q_UNUSED(verticesPerPatch); + qWarning() << "Tessellation not supported with OpenGL ES 2"; +} + +void GraphicsHelperES2::useProgram(GLuint programId) +{ + m_funcs->glUseProgram(programId); +} + +QVector<ShaderUniform> GraphicsHelperES2::programUniformsAndLocations(GLuint programId) +{ + QVector<ShaderUniform> uniforms; + + GLint nbrActiveUniforms = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms); + uniforms.reserve(nbrActiveUniforms); + char uniformName[256]; + for (GLint i = 0; i < nbrActiveUniforms; i++) { + ShaderUniform uniform; + GLsizei uniformNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength, + &uniform.m_size, &uniform.m_type, uniformName); + uniformName[sizeof(uniformName) - 1] = '\0'; + uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName); + uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength); + // Work around for uniform array names that aren't returned with [0] by some drivers + if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]"))) + uniform.m_name.append(QLatin1String("[0]")); + uniform.m_rawByteSize = uniformByteSize(uniform); + uniforms.append(uniform); + } + return uniforms; +} + +QVector<ShaderAttribute> GraphicsHelperES2::programAttributesAndLocations(GLuint programId) +{ + QVector<ShaderAttribute> attributes; + GLint nbrActiveAttributes = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes); + attributes.reserve(nbrActiveAttributes); + char attributeName[256]; + for (GLint i = 0; i < nbrActiveAttributes; i++) { + ShaderAttribute attribute; + GLsizei attributeNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength, + &attribute.m_size, &attribute.m_type, attributeName); + attributeName[sizeof(attributeName) - 1] = '\0'; + attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName); + attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength); + attributes.append(attribute); + } + return attributes; +} + +QVector<ShaderUniformBlock> GraphicsHelperES2::programUniformBlocks(GLuint programId) +{ + Q_UNUSED(programId); + QVector<ShaderUniformBlock> blocks; + qWarning() << "UBO are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; + return blocks; +} + +QVector<ShaderStorageBlock> GraphicsHelperES2::programShaderStorageBlocks(GLuint programId) +{ + Q_UNUSED(programId); + QVector<ShaderStorageBlock> blocks; + qWarning() << "SSBO are not supported by OpenGL ES 2.0 (since OpenGL ES 3.1)"; + return blocks; +} + +void GraphicsHelperES2::vertexAttribDivisor(GLuint index, GLuint divisor) +{ + Q_UNUSED(index); + Q_UNUSED(divisor); +} + +void GraphicsHelperES2::vertexAttributePointer(GLenum shaderDataType, + GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer) +{ + switch (shaderDataType) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT4: + m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer); + break; + + default: + qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type"; + Q_UNREACHABLE(); + } +} + +void GraphicsHelperES2::readBuffer(GLenum mode) +{ + Q_UNUSED(mode) + qWarning() << "glReadBuffer not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)"; +} + +void GraphicsHelperES2::drawBuffer(GLenum mode) +{ + Q_UNUSED(mode); + qWarning() << "glDrawBuffer is not supported with OpenGL ES 2"; +} + +void GraphicsHelperES2::blendEquation(GLenum mode) +{ + m_funcs->glBlendEquation(mode); +} + +void GraphicsHelperES2::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) +{ + Q_UNUSED(buf); + Q_UNUSED(sfactor); + Q_UNUSED(dfactor); + + qWarning() << "glBlendFunci() not supported by OpenGL ES 2.0"; +} + +void GraphicsHelperES2::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) +{ + Q_UNUSED(buf); + Q_UNUSED(sRGB); + Q_UNUSED(dRGB); + Q_UNUSED(sAlpha); + Q_UNUSED(dAlpha); + + qWarning() << "glBlendFuncSeparatei() not supported by OpenGL ES 2.0"; +} + +void GraphicsHelperES2::alphaTest(GLenum, GLenum) +{ + qCWarning(Render::Rendering) << Q_FUNC_INFO << "AlphaTest not available with OpenGL ES 2.0"; +} + +void GraphicsHelperES2::depthTest(GLenum mode) +{ + m_funcs->glEnable(GL_DEPTH_TEST); + m_funcs->glDepthFunc(mode); +} + +void GraphicsHelperES2::depthMask(GLenum mode) +{ + m_funcs->glDepthMask(mode); +} + +void GraphicsHelperES2::frontFace(GLenum mode) +{ + m_funcs->glFrontFace(mode); +} + +void GraphicsHelperES2::setMSAAEnabled(bool enabled) +{ + Q_UNUSED(enabled); + qWarning() << "MSAA not available with OpenGL ES 2.0"; +} + +void GraphicsHelperES2::setAlphaCoverageEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE) + : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); +} + +GLuint GraphicsHelperES2::createFrameBufferObject() +{ + GLuint id; + m_funcs->glGenFramebuffers(1, &id); + return id; +} + +void GraphicsHelperES2::releaseFrameBufferObject(GLuint frameBufferId) +{ + m_funcs->glDeleteFramebuffers(1, &frameBufferId); +} + +void GraphicsHelperES2::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) +{ + switch (mode) { + case FBODraw: + m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId); + return; + case FBORead: + m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId); + return; + case FBOReadAndDraw: + default: + m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId); + return; + } +} + +GLuint GraphicsHelperES2::boundFrameBufferObject() +{ + GLint id = 0; + m_funcs->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &id); + return id; +} + +bool GraphicsHelperES2::checkFrameBufferComplete() +{ + return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +} + +bool GraphicsHelperES2::frameBufferNeedsRenderBuffer(const Attachment &attachment) +{ + // Use a renderbuffer for combined depth+stencil attachments since this is + // problematic before GLES 3.2. Keep using textures for everything else. + return attachment.m_point == QRenderTargetOutput::DepthStencil; +} + +void GraphicsHelperES2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) +{ + GLenum attr = GL_COLOR_ATTACHMENT0; + + if (attachment.m_point == QRenderTargetOutput::Color0) + attr = GL_COLOR_ATTACHMENT0; + else if (attachment.m_point == QRenderTargetOutput::Depth) + attr = GL_DEPTH_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::Stencil) + attr = GL_STENCIL_ATTACHMENT; + else + qCritical() << "Unsupported FBO attachment OpenGL ES 2.0"; + + const QOpenGLTexture::Target target = texture->target(); + + if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face == QAbstractTexture::AllFaces) { + qWarning() << "OpenGL ES 2.0 doesn't handle attaching all the faces of a cube map texture at once to an FBO"; + return; + } + + texture->bind(); + if (target == QOpenGLTexture::Target2D) + m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel); + else if (target == QOpenGLTexture::TargetCubeMap) + m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel); + else + qCritical() << "Unsupported Texture FBO attachment format"; + texture->release(); +} + +void GraphicsHelperES2::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) +{ + if (attachment.m_point != QRenderTargetOutput::DepthStencil) { + qCritical() << "Renderbuffers only supported for combined depth-stencil, but got attachment point" + << attachment.m_point; + return; + } + + renderBuffer->bind(); + m_funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->renderBufferId()); + m_funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->renderBufferId()); + renderBuffer->release(); +} + +bool GraphicsHelperES2::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case RenderBufferDimensionRetrieval: + return true; + case BlitFramebuffer: + return m_supportFramebufferBlit; + default: + return false; + } +} +void GraphicsHelperES2::drawBuffers(GLsizei, const int *) +{ + qWarning() << "drawBuffers is not supported by ES 2.0"; +} + +void GraphicsHelperES2::bindFragDataLocation(GLuint , const QHash<QString, int> &) +{ + qCritical() << "bindFragDataLocation is not supported by ES 2.0"; +} + +void GraphicsHelperES2::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +{ + Q_UNUSED(programId); + Q_UNUSED(uniformBlockIndex); + Q_UNUSED(uniformBlockBinding); + qWarning() << "UBO are not supported by ES 2.0 (since ES 3.0)"; +} + +void GraphicsHelperES2::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) +{ + Q_UNUSED(programId); + Q_UNUSED(shaderStorageBlockIndex); + Q_UNUSED(shaderStorageBlockBinding); + qWarning() << "SSBO are not supported by ES 2.0 (since ES 3.1)"; +} + +void GraphicsHelperES2::bindBufferBase(GLenum target, GLuint index, GLuint buffer) +{ + Q_UNUSED(target); + Q_UNUSED(index); + Q_UNUSED(buffer); + qWarning() << "bindBufferBase is not supported by ES 2.0 (since ES 3.0)"; +} + +void GraphicsHelperES2::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + Q_UNUSED(v); + Q_UNUSED(description); + Q_UNUSED(buffer); + qWarning() << "UBO are not supported by ES 2.0 (since ES 3.0)"; +} + +uint GraphicsHelperES2::uniformByteSize(const ShaderUniform &description) +{ + uint rawByteSize = 0; + int arrayStride = qMax(description.m_arrayStride, 0); + int matrixStride = qMax(description.m_matrixStride, 0); + + switch (description.m_type) { + + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + rawByteSize = 8; + break; + + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + rawByteSize = 12; + break; + + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + rawByteSize = 16; + break; + + case GL_FLOAT_MAT2: + rawByteSize = matrixStride ? 2 * matrixStride : 16; + break; + + case GL_FLOAT_MAT3: + rawByteSize = matrixStride ? 3 * matrixStride : 36; + break; + + case GL_FLOAT_MAT4: + rawByteSize = matrixStride ? 4 * matrixStride : 64; + break; + + case GL_BOOL: + rawByteSize = 1; + break; + + case GL_BOOL_VEC2: + rawByteSize = 2; + break; + + case GL_BOOL_VEC3: + rawByteSize = 3; + break; + + case GL_BOOL_VEC4: + rawByteSize = 4; + break; + + case GL_INT: + case GL_FLOAT: + case GL_SAMPLER_2D: + case GL_SAMPLER_CUBE: + rawByteSize = 4; + break; + } + + return arrayStride ? rawByteSize * arrayStride : rawByteSize; +} + +void GraphicsHelperES2::enableClipPlane(int) +{ +} + +void GraphicsHelperES2::disableClipPlane(int) +{ +} + +void GraphicsHelperES2::setClipPlane(int, const QVector3D &, float) +{ + qWarning() << "Clip planes not supported by OpenGL ES 2.0"; +} + +GLint GraphicsHelperES2::maxClipPlaneCount() +{ + return 0; +} + +void GraphicsHelperES2::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + Q_UNUSED(barriers); + qWarning() << "memory barrier is not supported by OpenGL ES 2.0 (since 4.3)"; +} + +void GraphicsHelperES2::enablePrimitiveRestart(int) +{ +} + +void GraphicsHelperES2::enableVertexAttributeArray(int location) +{ + m_funcs->glEnableVertexAttribArray(location); +} + +void GraphicsHelperES2::disablePrimitiveRestart() +{ +} + +void GraphicsHelperES2::clearBufferf(GLint drawbuffer, const QVector4D &values) +{ + Q_UNUSED(drawbuffer); + Q_UNUSED(values); + qWarning() << "glClearBuffer*() not supported by OpenGL ES 2.0"; +} + +void GraphicsHelperES2::pointSize(bool programmable, GLfloat value) +{ + // If this is not a reset to default values, print a warning + if (programmable || !qFuzzyCompare(value, 1.0f)) { + static bool warned = false; + if (!warned) { + qWarning() << "glPointSize() and GL_PROGRAM_POINT_SIZE are not supported by ES 2.0"; + warned = true; + } + } +} + +void GraphicsHelperES2::enablei(GLenum cap, GLuint index) +{ + Q_UNUSED(cap); + Q_UNUSED(index); + qWarning() << "glEnablei() not supported by OpenGL ES 2.0"; +} + +void GraphicsHelperES2::disablei(GLenum cap, GLuint index) +{ + Q_UNUSED(cap); + Q_UNUSED(index); + qWarning() << "glDisablei() not supported by OpenGL ES 2.0"; +} + +void GraphicsHelperES2::setSeamlessCubemap(bool enable) +{ + Q_UNUSED(enable); + qWarning() << "GL_TEXTURE_CUBE_MAP_SEAMLESS not supported by OpenGL ES 2.0"; +} + +QSize GraphicsHelperES2::getRenderBufferDimensions(GLuint renderBufferId) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0); + + return QSize(width, height); +} + +QSize GraphicsHelperES2::getTextureDimensions(GLuint textureId, GLenum target, uint level) +{ + Q_UNUSED(textureId); + Q_UNUSED(target); + Q_UNUSED(level); + qCritical() << "getTextureDimensions is not supported by ES 2.0"; + return QSize(0, 0); +} + +void GraphicsHelperES2::dispatchCompute(GLuint wx, GLuint wy, GLuint wz) +{ + Q_UNUSED(wx); + Q_UNUSED(wy); + Q_UNUSED(wz); + qWarning() << "Compute Shaders are not supported by ES 2.0 (since ES 3.1)"; +} + +char *GraphicsHelperES2::mapBuffer(GLenum target, GLsizeiptr size) +{ + Q_UNUSED(target); + Q_UNUSED(size); + qWarning() << "Map buffer is not a core requirement for ES 2.0"; + return nullptr; +} + +GLboolean GraphicsHelperES2::unmapBuffer(GLenum target) +{ + Q_UNUSED(target); + qWarning() << "unMap buffer is not a core requirement for ES 2.0"; + return false; +} + +void GraphicsHelperES2::glUniform1fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform1fv(location, count, values); +} + +void GraphicsHelperES2::glUniform2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform2fv(location, count, values); +} + +void GraphicsHelperES2::glUniform3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform3fv(location, count, values); +} + +void GraphicsHelperES2::glUniform4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform4fv(location, count, values); +} + +void GraphicsHelperES2::glUniform1iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform1iv(location, count, values); +} + +void GraphicsHelperES2::glUniform2iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform2iv(location, count, values); +} + +void GraphicsHelperES2::glUniform3iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform3iv(location, count, values); +} + +void GraphicsHelperES2::glUniform4iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform4iv(location, count, values); +} + +void GraphicsHelperES2::glUniform1uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform1uiv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniform2uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform2uiv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniform3uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform3uiv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniform4uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform4uiv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2fv(location, count, false, values); +} + +void GraphicsHelperES2::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3fv(location, count, false, values); +} + +void GraphicsHelperES2::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4fv(location, count, false, values); +} + +void GraphicsHelperES2::glUniformMatrix2x3fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix2x3fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix3x2fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix3x2fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix2x4fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix2x4fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix4x2fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix4x2fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix3x4fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix3x4fv not supported by ES 2"; +} + +void GraphicsHelperES2::glUniformMatrix4x3fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix4x3fv not supported by ES 2"; +} + +UniformType GraphicsHelperES2::uniformTypeFromGLType(GLenum type) +{ + switch (type) { + case GL_FLOAT: + return UniformType::Float; + case GL_FLOAT_VEC2: + return UniformType::Vec2; + case GL_FLOAT_VEC3: + return UniformType::Vec3; + case GL_FLOAT_VEC4: + return UniformType::Vec4; + case GL_FLOAT_MAT2: + return UniformType::Mat2; + case GL_FLOAT_MAT3: + return UniformType::Mat3; + case GL_FLOAT_MAT4: + return UniformType::Mat4; + case GL_INT: + return UniformType::Int; + case GL_INT_VEC2: + return UniformType::IVec2; + case GL_INT_VEC3: + return UniformType::IVec3; + case GL_INT_VEC4: + return UniformType::IVec4; + case GL_BOOL: + return UniformType::Bool; + case GL_BOOL_VEC2: + return UniformType::BVec2; + case GL_BOOL_VEC3: + return UniformType::BVec3; + case GL_BOOL_VEC4: + return UniformType::BVec4; + + case GL_SAMPLER_2D: + case GL_SAMPLER_CUBE: + return UniformType::Sampler; + default: + Q_UNREACHABLE(); + return UniformType::Float; + } +} + +void GraphicsHelperES2::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) +{ + if (!m_supportFramebufferBlit) + qWarning() << "Framebuffer blits are not supported by ES 2.0 (since ES 3.1)"; + else + m_ext->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h new file mode 100644 index 000000000..1c6df41b6 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERES2_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERES2_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <QOpenGLContext> +#include <QOpenGLFunctions> + +QT_BEGIN_NAMESPACE + +class QOpenGLExtensions; + +namespace Qt3DRender { +namespace Render { + +class GraphicsHelperES2 : public GraphicsHelperInterface +{ +public: + GraphicsHelperES2(); + virtual ~GraphicsHelperES2(); + + // QGraphicHelperInterface interface + void alphaTest(GLenum mode1, GLenum mode2) override; + void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override; + void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override; + void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override; + void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override; + void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override; + void blendEquation(GLenum mode) override; + void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override; + void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override; + void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override; + GLuint boundFrameBufferObject() override; + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override; + bool checkFrameBufferComplete() override; + void clearBufferf(GLint drawbuffer, const QVector4D &values) override; + GLuint createFrameBufferObject() override; + void depthMask(GLenum mode) override; + void depthTest(GLenum mode) override; + void disableClipPlane(int clipPlane) override; + void disablei(GLenum cap, GLuint index) override; + void disablePrimitiveRestart() override; + void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override; + char *mapBuffer(GLenum target, GLsizeiptr size) override; + GLboolean unmapBuffer(GLenum target) override; + void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override; + void drawArraysIndirect(GLenum mode,void *indirect) override; + void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override; + void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override; + void drawBuffers(GLsizei n, const int *bufs) override; + void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override; + void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override; + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override; + void enableClipPlane(int clipPlane) override; + void enablei(GLenum cap, GLuint index) override; + void enablePrimitiveRestart(int primitiveRestartIndex) override; + void enableVertexAttributeArray(int location) override; + void frontFace(GLenum mode) override; + QSize getRenderBufferDimensions(GLuint renderBufferId) override; + QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override; + void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override; + void pointSize(bool programmable, GLfloat value) override; + GLint maxClipPlaneCount() override; + void memoryBarrier(QMemoryBarrier::Operations barriers) override; + QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override; + QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override; + QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override; + QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override; + void releaseFrameBufferObject(GLuint frameBufferId) override; + void setMSAAEnabled(bool enable) override; + void setAlphaCoverageEnabled(bool enable) override; + void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override; + void setSeamlessCubemap(bool enable) override; + void setVerticesPerPatch(GLint verticesPerPatch) override; + bool supportsFeature(Feature feature) const override; + uint uniformByteSize(const ShaderUniform &description) override; + void useProgram(GLuint programId) override; + void vertexAttribDivisor(GLuint index, GLuint divisor) override; + void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override; + void readBuffer(GLenum mode) override; + void drawBuffer(GLenum mode) override; + + void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override; + + void glUniform1iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform2iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform3iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform4iv(GLint location, GLsizei count, const GLint *value) override; + + void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override; + + void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override; + + UniformType uniformTypeFromGLType(GLenum glType) override; + +protected: + QOpenGLFunctions *m_funcs; + bool m_supportFramebufferBlit; + QScopedPointer<QOpenGLExtensions> m_ext; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES2_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp new file mode 100644 index 000000000..813c627b8 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp @@ -0,0 +1,574 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 Svenn-Arne Dragly. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicshelperes3_p.h" +#include <private/attachmentpack_p.h> +#include <private/qgraphicsutils_p.h> +#include <private/renderlogging_p.h> +#include <QOpenGLExtraFunctions> + +QT_BEGIN_NAMESPACE + +// ES 3.0+ +#ifndef GL_SAMPLER_3D +#define GL_SAMPLER_3D 0x8B5F +#endif +#ifndef GL_SAMPLER_2D_SHADOW +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#endif +#ifndef GL_SAMPLER_CUBE_SHADOW +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#endif +#ifndef GL_SAMPLER_2D_ARRAY +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#endif +#ifndef GL_SAMPLER_2D_ARRAY_SHADOW +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#endif +#ifndef GL_FLOAT_MAT2x3 +#define GL_FLOAT_MAT2x3 0x8B65 +#endif +#ifndef GL_FLOAT_MAT2x4 +#define GL_FLOAT_MAT2x4 0x8B66 +#endif +#ifndef GL_FLOAT_MAT3x2 +#define GL_FLOAT_MAT3x2 0x8B67 +#endif +#ifndef GL_FLOAT_MAT3x4 +#define GL_FLOAT_MAT3x4 0x8B68 +#endif +#ifndef GL_FLOAT_MAT4x2 +#define GL_FLOAT_MAT4x2 0x8B69 +#endif +#ifndef GL_FLOAT_MAT4x3 +#define GL_FLOAT_MAT4x3 0x8B6A +#endif +#ifndef GL_UNSIGNED_INT_VEC2 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#endif +#ifndef GL_UNSIGNED_INT_VEC3 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#endif +#ifndef GL_UNSIGNED_INT_VEC4 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#endif +#ifndef GL_INT_SAMPLER_2D +#define GL_INT_SAMPLER_2D 0x8DCA +#endif +#ifndef GL_INT_SAMPLER_3D +#define GL_INT_SAMPLER_3D 0x8DCB +#endif +#ifndef GL_INT_SAMPLER_CUBE +#define GL_INT_SAMPLER_CUBE 0x8DCC +#endif +#ifndef GL_INT_SAMPLER_2D_ARRAY +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#endif +#ifndef GL_UNSIGNED_INT_SAMPLER_2D +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#endif +#ifndef GL_UNSIGNED_INT_SAMPLER_3D +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#endif +#ifndef GL_UNSIGNED_INT_SAMPLER_CUBE +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#endif +#ifndef GL_UNSIGNED_INT_SAMPLER_2D_ARRAY +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#endif + +#ifndef GL_ACTIVE_UNIFORM_BLOCKS +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#endif +#ifndef GL_UNIFORM_BLOCK_INDEX +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#endif +#ifndef GL_UNIFORM_OFFSET +#define GL_UNIFORM_OFFSET 0x8A3B +#endif +#ifndef GL_UNIFORM_ARRAY_STRIDE +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#endif +#ifndef GL_UNIFORM_MATRIX_STRIDE +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#endif +#ifndef GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#endif +#ifndef GL_UNIFORM_BLOCK_BINDING +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#endif +#ifndef GL_UNIFORM_BLOCK_DATA_SIZE +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#endif + +namespace Qt3DRender { +namespace Render { + +GraphicsHelperES3::GraphicsHelperES3() +{ +} + +GraphicsHelperES3::~GraphicsHelperES3() +{ +} + +void GraphicsHelperES3::initializeHelper(QOpenGLContext *context, + QAbstractOpenGLFunctions *functions) +{ + GraphicsHelperES2::initializeHelper(context, functions); + m_extraFuncs = context->extraFunctions(); + Q_ASSERT(m_extraFuncs); +} + +void GraphicsHelperES3::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLsizei instances, + GLint baseVertex, + GLint baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 3"; + + if (baseVertex != 0) + qWarning() << "glDrawElementsInstancedBaseVertex is not supported with OpenGL ES 3"; + + m_extraFuncs->glDrawElementsInstanced(primitiveType, + primitiveCount, + indexType, + indices, + instances); +} + +void GraphicsHelperES3::vertexAttribDivisor(GLuint index, GLuint divisor) +{ + m_extraFuncs->glVertexAttribDivisor(index, divisor); +} + +void GraphicsHelperES3::vertexAttributePointer(GLenum shaderDataType, + GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer) +{ + switch (shaderDataType) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + case GL_FLOAT_MAT4: + m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer); + break; + + case GL_INT: + case GL_INT_VEC2: + case GL_INT_VEC3: + case GL_INT_VEC4: + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_VEC2: + case GL_UNSIGNED_INT_VEC3: + case GL_UNSIGNED_INT_VEC4: + m_extraFuncs->glVertexAttribIPointer(index, size, type, stride, pointer); + break; + + default: + qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type"; + Q_UNREACHABLE(); + } +} + +void GraphicsHelperES3::readBuffer(GLenum mode) +{ + m_extraFuncs->glReadBuffer(mode); +} + +void GraphicsHelperES3::drawBuffer(GLenum mode) +{ + Q_UNUSED(mode); + qWarning() << "glDrawBuffer is not supported with OpenGL ES 3"; +} + +void GraphicsHelperES3::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) +{ + GLenum attr = GL_COLOR_ATTACHMENT0; + + if (attachment.m_point <= QRenderTargetOutput::Color15) + attr = GL_COLOR_ATTACHMENT0 + attachment.m_point; + else if (attachment.m_point == QRenderTargetOutput::Depth) + attr = GL_DEPTH_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::Stencil) + attr = GL_STENCIL_ATTACHMENT; + else + qCritical() << "Unsupported FBO attachment OpenGL ES 3.0"; + + const QOpenGLTexture::Target target = texture->target(); + + if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face == QAbstractTexture::AllFaces) { + qWarning() << "OpenGL ES 3.0 doesn't handle attaching all the faces of a cube map texture at once to an FBO"; + return; + } + + texture->bind(); + if (target == QOpenGLTexture::Target2D) + m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel); + else if (target == QOpenGLTexture::TargetCubeMap) + m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel); + else + qCritical() << "Unsupported Texture FBO attachment format"; + texture->release(); +} + +bool GraphicsHelperES3::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case RenderBufferDimensionRetrieval: + case MRT: + case BlitFramebuffer: + case UniformBufferObject: + case MapBuffer: + return true; + default: + return false; + } +} + +void GraphicsHelperES3::drawBuffers(GLsizei n, const int *bufs) +{ + QVarLengthArray<GLenum, 16> drawBufs(n); + + for (int i = 0; i < n; i++) + drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i]; + m_extraFuncs->glDrawBuffers(n, drawBufs.constData()); +} + +UniformType GraphicsHelperES3::uniformTypeFromGLType(GLenum glType) +{ + switch (glType) { + case GL_SAMPLER_3D: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + return UniformType::Sampler; + default: + return GraphicsHelperES2::uniformTypeFromGLType(glType); + } +} + +void GraphicsHelperES3::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) +{ + m_extraFuncs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +void GraphicsHelperES3::bindBufferBase(GLenum target, GLuint index, GLuint buffer) +{ + m_extraFuncs->glBindBufferBase(target, index, buffer); +} + +void GraphicsHelperES3::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +{ + m_extraFuncs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding); +} + +void GraphicsHelperES3::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + char *bufferData = buffer.data(); + + switch (description.m_type) { + + case GL_FLOAT: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_FLOAT_VEC2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_FLOAT_VEC3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_FLOAT_VEC4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_FLOAT_MAT2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2); + break; + } + + case GL_FLOAT_MAT2x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3); + break; + } + + case GL_FLOAT_MAT2x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4); + break; + } + + case GL_FLOAT_MAT3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3); + break; + } + + case GL_FLOAT_MAT3x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2); + break; + } + + case GL_FLOAT_MAT3x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4); + break; + } + + case GL_FLOAT_MAT4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4); + break; + } + + case GL_FLOAT_MAT4x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2); + break; + } + + case GL_FLOAT_MAT4x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3); + break; + } + + case GL_INT: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_INT_VEC2: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_INT_VEC3: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_INT_VEC4: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_UNSIGNED_INT: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_UNSIGNED_INT_VEC2: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_UNSIGNED_INT_VEC3: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_UNSIGNED_INT_VEC4: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_BOOL: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_BOOL_VEC2: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_BOOL_VEC3: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_BOOL_VEC4: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + // note: only GLES 3.0 supported types, not the same as OpenGL proper + // (also, no MS samplers before ES 3.1) + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + { + Q_ASSERT(description.m_size == 1); + int value = v.toInt(); + QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1); + break; + } + + default: + qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name; + break; + } +} + +char *GraphicsHelperES3::mapBuffer(GLenum target, GLsizeiptr size) +{ + return static_cast<char*>(m_extraFuncs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); +} + +GLboolean GraphicsHelperES3::unmapBuffer(GLenum target) +{ + return m_extraFuncs->glUnmapBuffer(target); +} + +QVector<ShaderUniform> GraphicsHelperES3::programUniformsAndLocations(GLuint programId) +{ + QVector<ShaderUniform> uniforms; + + GLint nbrActiveUniforms = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms); + uniforms.reserve(nbrActiveUniforms); + char uniformName[256]; + for (GLint i = 0; i < nbrActiveUniforms; i++) { + ShaderUniform uniform; + GLsizei uniformNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength, + &uniform.m_size, &uniform.m_type, uniformName); + uniformName[sizeof(uniformName) - 1] = '\0'; + uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName); + uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength); + m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex); + m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset); + m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride); + m_extraFuncs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride); + uniform.m_rawByteSize = uniformByteSize(uniform); + uniforms.append(uniform); + qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size + << " offset" << uniform.m_offset + << " rawSize" << uniform.m_rawByteSize; + } + + return uniforms; +} + +QVector<ShaderUniformBlock> GraphicsHelperES3::programUniformBlocks(GLuint programId) +{ + QVector<ShaderUniformBlock> blocks; + GLint nbrActiveUniformsBlocks = 0; + m_extraFuncs->glGetProgramiv(programId, GL_ACTIVE_UNIFORM_BLOCKS, &nbrActiveUniformsBlocks); + blocks.reserve(nbrActiveUniformsBlocks); + for (GLint i = 0; i < nbrActiveUniformsBlocks; i++) { + QByteArray uniformBlockName(256, '\0'); + GLsizei length = 0; + ShaderUniformBlock uniformBlock; + m_extraFuncs->glGetActiveUniformBlockName(programId, i, 256, &length, uniformBlockName.data()); + uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length)); + uniformBlock.m_index = i; + m_extraFuncs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformBlock.m_activeUniformsCount); + m_extraFuncs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_BINDING, &uniformBlock.m_binding); + m_extraFuncs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_DATA_SIZE, &uniformBlock.m_size); + blocks.append(uniformBlock); + } + return blocks; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp new file mode 100644 index 000000000..6290d091d --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicshelperes3_2_p.h" +#include <QOpenGLExtraFunctions> +#include <Qt3DRender/qrendertargetoutput.h> +#include <private/attachmentpack_p.h> + +QT_BEGIN_NAMESPACE + +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +#ifndef GL_DEPTH_STENCIL_ATTACHMENT +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#endif + +namespace Qt3DRender { +namespace Render { + +GraphicsHelperES3_2::GraphicsHelperES3_2() +{ +} + +GraphicsHelperES3_2::~GraphicsHelperES3_2() +{ +} + +bool GraphicsHelperES3_2::frameBufferNeedsRenderBuffer(const Attachment &attachment) +{ + Q_UNUSED(attachment); + // This is first ES version where we have glFramebufferTexture, so + // attaching a D24S8 texture to the combined depth-stencil attachment point + // should work. + return false; +} + +void GraphicsHelperES3_2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) +{ + GLenum attr = GL_COLOR_ATTACHMENT0; + + if (attachment.m_point <= QRenderTargetOutput::Color15) + attr = GL_COLOR_ATTACHMENT0 + attachment.m_point; + else if (attachment.m_point == QRenderTargetOutput::Depth) + attr = GL_DEPTH_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::Stencil) + attr = GL_STENCIL_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::DepthStencil) + attr = GL_DEPTH_STENCIL_ATTACHMENT; + else + qCritical() << "Unsupported FBO attachment OpenGL ES 3.2"; + + const QOpenGLTexture::Target target = texture->target(); + + texture->bind(); + if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face != QAbstractTexture::AllFaces) + m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel); + else + m_extraFuncs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel); + texture->release(); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h new file mode 100644 index 000000000..018db6481 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_2_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERES3_2_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERES3_2_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/graphicshelperes3_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class GraphicsHelperES3_2 : public GraphicsHelperES3 +{ +public: + GraphicsHelperES3_2(); + ~GraphicsHelperES3_2(); + + // QGraphicHelperInterface interface + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES3_2_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h new file mode 100644 index 000000000..9bca2d48d --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 Svenn-Arne Dragly. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERES3_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERES3_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/graphicshelperes2_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class GraphicsHelperES3 : public GraphicsHelperES2 +{ +public: + GraphicsHelperES3(); + ~GraphicsHelperES3(); + + // QGraphicHelperInterface interface + void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override; + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override; + void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override; + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override; + void drawBuffers(GLsizei n, const int *bufs) override; + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override; + void readBuffer(GLenum mode) override; + void drawBuffer(GLenum mode) override; + void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override; + char *mapBuffer(GLenum target, GLsizeiptr size) override; + QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override; + QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override; + bool supportsFeature(Feature feature) const override; + GLboolean unmapBuffer(GLenum target) override; + void vertexAttribDivisor(GLuint index, GLuint divisor) override; + void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override; + + UniformType uniformTypeFromGLType(GLenum glType) override; + +protected: + QOpenGLExtraFunctions *m_extraFuncs = nullptr; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERES3_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp new file mode 100644 index 000000000..6da8a9b6f --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp @@ -0,0 +1,882 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicshelpergl2_p.h" +#ifndef QT_OPENGL_ES_2 +#include <QOpenGLFunctions_2_0> +#include <private/attachmentpack_p.h> +#include <QtOpenGLExtensions/QOpenGLExtensions> +#include <private/qgraphicsutils_p.h> +#include <Qt3DRender/private/renderlogging_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +GraphicsHelperGL2::GraphicsHelperGL2() + : m_funcs(nullptr) + , m_fboFuncs(nullptr) +{ + +} + +void GraphicsHelperGL2::initializeHelper(QOpenGLContext *context, + QAbstractOpenGLFunctions *functions) +{ + Q_UNUSED(context); + m_funcs = static_cast<QOpenGLFunctions_2_0*>(functions); + const bool ok = m_funcs->initializeOpenGLFunctions(); + Q_ASSERT(ok); + Q_UNUSED(ok); + if (context->hasExtension(QByteArrayLiteral("GL_ARB_framebuffer_object"))) { + m_fboFuncs = new QOpenGLExtension_ARB_framebuffer_object(); + const bool extensionOk = m_fboFuncs->initializeOpenGLFunctions(); + Q_ASSERT(extensionOk); + Q_UNUSED(extensionOk); + } +} + +void GraphicsHelperGL2::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLsizei instances, + GLint baseVertex, + GLint baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2"; + + if (baseVertex != 0) + qWarning() << "glDrawElementsInstancedBaseVertex is not supported with OpenGL ES 2"; + + for (GLint i = 0; i < instances; i++) + drawElements(primitiveType, + primitiveCount, + indexType, + indices); +} + +void GraphicsHelperGL2::drawArraysInstanced(GLenum primitiveType, + GLint first, + GLsizei count, + GLsizei instances) +{ + for (GLint i = 0; i < instances; i++) + drawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperGL2::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL 2"; + for (GLint i = 0; i < instances; i++) + drawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperGL2::drawElements(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLint baseVertex) +{ + if (baseVertex != 0) + qWarning() << "glDrawElementsBaseVertex is not supported with OpenGL 2"; + + m_funcs->glDrawElements(primitiveType, + primitiveCount, + indexType, + indices); +} + +void GraphicsHelperGL2::drawArrays(GLenum primitiveType, + GLint first, + GLsizei count) +{ + m_funcs->glDrawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperGL2::drawElementsIndirect(GLenum, GLenum, void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL 2"; +} + +void GraphicsHelperGL2::drawArraysIndirect(GLenum , void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL 2"; +} + +void GraphicsHelperGL2::setVerticesPerPatch(GLint verticesPerPatch) +{ + Q_UNUSED(verticesPerPatch); + qWarning() << "Tessellation not supported with OpenGL 2"; +} + +void GraphicsHelperGL2::useProgram(GLuint programId) +{ + m_funcs->glUseProgram(programId); +} + +QVector<ShaderUniform> GraphicsHelperGL2::programUniformsAndLocations(GLuint programId) +{ + QVector<ShaderUniform> uniforms; + + GLint nbrActiveUniforms = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms); + uniforms.reserve(nbrActiveUniforms); + char uniformName[256]; + for (GLint i = 0; i < nbrActiveUniforms; i++) { + ShaderUniform uniform; + GLsizei uniformNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength, + &uniform.m_size, &uniform.m_type, uniformName); + uniformName[sizeof(uniformName) - 1] = '\0'; + uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName); + uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength); + // Work around for uniform array names that aren't returned with [0] by some drivers + if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]"))) + uniform.m_name.append(QLatin1String("[0]")); + uniform.m_rawByteSize = uniformByteSize(uniform); + uniforms.append(uniform); + } + return uniforms; +} + +QVector<ShaderAttribute> GraphicsHelperGL2::programAttributesAndLocations(GLuint programId) +{ + QVector<ShaderAttribute> attributes; + GLint nbrActiveAttributes = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes); + attributes.reserve(nbrActiveAttributes); + char attributeName[256]; + for (GLint i = 0; i < nbrActiveAttributes; i++) { + ShaderAttribute attribute; + GLsizei attributeNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength, + &attribute.m_size, &attribute.m_type, attributeName); + attributeName[sizeof(attributeName) - 1] = '\0'; + attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName); + attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength); + attributes.append(attribute); + } + return attributes; +} + +QVector<ShaderUniformBlock> GraphicsHelperGL2::programUniformBlocks(GLuint programId) +{ + Q_UNUSED(programId); + QVector<ShaderUniformBlock> blocks; + qWarning() << "UBO are not supported by OpenGL 2.0 (since OpenGL 3.1)"; + return blocks; +} + +QVector<ShaderStorageBlock> GraphicsHelperGL2::programShaderStorageBlocks(GLuint programId) +{ + Q_UNUSED(programId); + qWarning() << "SSBO are not supported by OpenGL 2.0 (since OpenGL 4.3)"; + return QVector<ShaderStorageBlock>(); +} + +void GraphicsHelperGL2::vertexAttribDivisor(GLuint index, + GLuint divisor) +{ + Q_UNUSED(index); + Q_UNUSED(divisor); +} + +void GraphicsHelperGL2::vertexAttributePointer(GLenum shaderDataType, + GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer) +{ + switch (shaderDataType) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + case GL_FLOAT_MAT4: + m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer); + break; + + default: + qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type"; + Q_UNREACHABLE(); + } +} + +void GraphicsHelperGL2::readBuffer(GLenum mode) +{ + m_funcs->glReadBuffer(mode); +} + +void GraphicsHelperGL2::drawBuffer(GLenum mode) +{ + m_funcs->glDrawBuffer(mode); +} + +void GraphicsHelperGL2::blendEquation(GLenum mode) +{ + m_funcs->glBlendEquation(mode); +} + +void GraphicsHelperGL2::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) +{ + Q_UNUSED(buf); + Q_UNUSED(sfactor); + Q_UNUSED(dfactor); + + qWarning() << "glBlendFunci() not supported by OpenGL 2.0 (since OpenGL 4.0)"; +} + +void GraphicsHelperGL2::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) +{ + Q_UNUSED(buf); + Q_UNUSED(sRGB); + Q_UNUSED(dRGB); + Q_UNUSED(sAlpha); + Q_UNUSED(dAlpha); + + qWarning() << "glBlendFuncSeparatei() not supported by OpenGL 2.0 (since OpenGL 4.0)"; +} + +void GraphicsHelperGL2::alphaTest(GLenum mode1, GLenum mode2) +{ + m_funcs->glEnable(GL_ALPHA_TEST); + m_funcs->glAlphaFunc(mode1, mode2); +} + +void GraphicsHelperGL2::depthTest(GLenum mode) +{ + m_funcs->glEnable(GL_DEPTH_TEST); + m_funcs->glDepthFunc(mode); +} + +void GraphicsHelperGL2::depthMask(GLenum mode) +{ + m_funcs->glDepthMask(mode); +} + +void GraphicsHelperGL2::frontFace(GLenum mode) +{ + m_funcs->glFrontFace(mode); +} + +void GraphicsHelperGL2::setMSAAEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_MULTISAMPLE) + : m_funcs->glDisable(GL_MULTISAMPLE); +} + +void GraphicsHelperGL2::setAlphaCoverageEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE) + : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); +} + +GLuint GraphicsHelperGL2::createFrameBufferObject() +{ + if (m_fboFuncs != nullptr) { + GLuint id; + m_fboFuncs->glGenFramebuffers(1, &id); + return id; + } + qWarning() << "FBO not supported by your OpenGL hardware"; + return 0; +} + +void GraphicsHelperGL2::releaseFrameBufferObject(GLuint frameBufferId) +{ + if (m_fboFuncs != nullptr) + m_fboFuncs->glDeleteFramebuffers(1, &frameBufferId); + else + qWarning() << "FBO not supported by your OpenGL hardware"; +} + +bool GraphicsHelperGL2::checkFrameBufferComplete() +{ + if (m_fboFuncs != nullptr) + return (m_fboFuncs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + return false; +} + +bool GraphicsHelperGL2::frameBufferNeedsRenderBuffer(const Attachment &attachment) +{ + Q_UNUSED(attachment); + return false; +} + +void GraphicsHelperGL2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) +{ + if (m_fboFuncs != nullptr) { + GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT; + + if (attachment.m_point <= QRenderTargetOutput::Color15) + attr = GL_COLOR_ATTACHMENT0 + attachment.m_point; + else if (attachment.m_point == QRenderTargetOutput::Depth) + attr = GL_DEPTH_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::Stencil) + attr = GL_STENCIL_ATTACHMENT; + else + qCritical() << "DepthStencil Attachment not supported on OpenGL 2.0"; + + const QOpenGLTexture::Target target = texture->target(); + + if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face == QAbstractTexture::AllFaces) { + qWarning() << "OpenGL 2.0 doesn't handle attaching all the faces of a cube map texture at once to an FBO"; + return; + } + + texture->bind(); + if (target == QOpenGLTexture::Target3D) + m_fboFuncs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel, attachment.m_layer); + else if (target == QOpenGLTexture::TargetCubeMap) + m_fboFuncs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel); + else if (target == QOpenGLTexture::Target1D) + m_fboFuncs->glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel); + else if (target == QOpenGLTexture::Target2D || target == QOpenGLTexture::TargetRectangle) + m_fboFuncs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, target, texture->textureId(), attachment.m_mipLevel); + else + qCritical() << "Texture format not supported for Attachment on OpenGL 2.0"; + texture->release(); + } +} + +void GraphicsHelperGL2::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) +{ + Q_UNUSED(renderBuffer); + Q_UNUSED(attachment); + Q_UNREACHABLE(); +} + +bool GraphicsHelperGL2::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case MRT: + return (m_fboFuncs != nullptr); + case TextureDimensionRetrieval: + return true; + default: + return false; + } +} + +void GraphicsHelperGL2::drawBuffers(GLsizei n, const int *bufs) +{ + QVarLengthArray<GLenum, 16> drawBufs(n); + + for (int i = 0; i < n; i++) + drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i]; + m_funcs->glDrawBuffers(n, drawBufs.constData()); +} + +void GraphicsHelperGL2::bindFragDataLocation(GLuint, const QHash<QString, int> &) +{ + qCritical() << "bindFragDataLocation is not supported by GL 2.0"; +} + +void GraphicsHelperGL2::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) +{ + if (m_fboFuncs != nullptr) { + switch (mode) { + case FBODraw: + m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId); + return; + case FBORead: + m_fboFuncs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId); + return; + case FBOReadAndDraw: + default: + m_fboFuncs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId); + return; + } + } else { + qWarning() << "FBO not supported by your OpenGL hardware"; + } +} + +GLuint GraphicsHelperGL2::boundFrameBufferObject() +{ + GLint id = 0; + m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id); + return id; +} + +void GraphicsHelperGL2::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +{ + Q_UNUSED(programId); + Q_UNUSED(uniformBlockIndex); + Q_UNUSED(uniformBlockBinding); + qWarning() << "UBO are not supported by OpenGL 2.0 (since OpenGL 3.1)"; +} + +void GraphicsHelperGL2::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) +{ + Q_UNUSED(programId); + Q_UNUSED(shaderStorageBlockIndex); + Q_UNUSED(shaderStorageBlockBinding); + qWarning() << "SSBO are not supported by OpenGL 2.0 (since OpenGL 4.3)"; +} + +void GraphicsHelperGL2::bindBufferBase(GLenum target, GLuint index, GLuint buffer) +{ + Q_UNUSED(target); + Q_UNUSED(index); + Q_UNUSED(buffer); + qWarning() << "bindBufferBase is not supported by OpenGL 2.0 (since OpenGL 3.0)"; +} + +void GraphicsHelperGL2::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + Q_UNUSED(v); + Q_UNUSED(description); + Q_UNUSED(buffer); + qWarning() << "UBO are not supported by OpenGL 2.0 (since OpenGL 3.1)"; +} + +uint GraphicsHelperGL2::uniformByteSize(const ShaderUniform &description) +{ + uint rawByteSize = 0; + int arrayStride = qMax(description.m_arrayStride, 0); + int matrixStride = qMax(description.m_matrixStride, 0); + + switch (description.m_type) { + + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + rawByteSize = 8; + break; + + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + rawByteSize = 12; + break; + + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + rawByteSize = 16; + break; + + case GL_FLOAT_MAT2: + rawByteSize = matrixStride ? 2 * matrixStride : 16; + break; + + case GL_FLOAT_MAT2x4: + rawByteSize = matrixStride ? 2 * matrixStride : 32; + break; + + case GL_FLOAT_MAT4x2: + rawByteSize = matrixStride ? 4 * matrixStride : 32; + break; + + case GL_FLOAT_MAT3: + rawByteSize = matrixStride ? 3 * matrixStride : 36; + break; + + case GL_FLOAT_MAT2x3: + rawByteSize = matrixStride ? 2 * matrixStride : 24; + break; + + case GL_FLOAT_MAT3x2: + rawByteSize = matrixStride ? 3 * matrixStride : 24; + break; + + case GL_FLOAT_MAT4: + rawByteSize = matrixStride ? 4 * matrixStride : 64; + break; + + case GL_FLOAT_MAT4x3: + rawByteSize = matrixStride ? 4 * matrixStride : 48; + break; + + case GL_FLOAT_MAT3x4: + rawByteSize = matrixStride ? 3 * matrixStride : 48; + break; + + case GL_BOOL: + rawByteSize = 1; + break; + + case GL_BOOL_VEC2: + rawByteSize = 2; + break; + + case GL_BOOL_VEC3: + rawByteSize = 3; + break; + + case GL_BOOL_VEC4: + rawByteSize = 4; + break; + + case GL_INT: + case GL_FLOAT: + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + rawByteSize = 4; + break; + + default: + Q_UNREACHABLE(); + } + + return arrayStride ? rawByteSize * arrayStride : rawByteSize; +} + +void GraphicsHelperGL2::enableClipPlane(int clipPlane) +{ + m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL2::disableClipPlane(int clipPlane) +{ + m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL2::setClipPlane(int clipPlane, const QVector3D &normal, float distance) +{ + double plane[4]; + plane[0] = normal.x(); + plane[1] = normal.y(); + plane[2] = normal.z(); + plane[3] = distance; + + m_funcs->glClipPlane(GL_CLIP_PLANE0 + clipPlane, plane); +} + +GLint GraphicsHelperGL2::maxClipPlaneCount() +{ + GLint max = 0; + m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max); + return max; +} + +void GraphicsHelperGL2::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + Q_UNUSED(barriers); + qWarning() << "memory barrier is not supported by OpenGL 2.0 (since 4.3)"; +} + +void GraphicsHelperGL2::enablePrimitiveRestart(int) +{ +} + +void GraphicsHelperGL2::enableVertexAttributeArray(int location) +{ + m_funcs->glEnableVertexAttribArray(location); +} + +void GraphicsHelperGL2::disablePrimitiveRestart() +{ +} + +void GraphicsHelperGL2::clearBufferf(GLint drawbuffer, const QVector4D &values) +{ + Q_UNUSED(drawbuffer); + Q_UNUSED(values); + qWarning() << "glClearBuffer*() not supported by OpenGL 2.0"; +} + +void GraphicsHelperGL2::pointSize(bool programmable, GLfloat value) +{ + m_funcs->glEnable(GL_POINT_SPRITE); + if (programmable) + m_funcs->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + else + m_funcs->glPointSize(value); +} + +void GraphicsHelperGL2::enablei(GLenum cap, GLuint index) +{ + Q_UNUSED(cap); + Q_UNUSED(index); + qWarning() << "glEnablei() not supported by OpenGL 2.0 (since 3.0)"; +} + +void GraphicsHelperGL2::disablei(GLenum cap, GLuint index) +{ + Q_UNUSED(cap); + Q_UNUSED(index); + qWarning() << "glDisablei() not supported by OpenGL 2.0 (since 3.0)"; +} + +void GraphicsHelperGL2::setSeamlessCubemap(bool enable) +{ + Q_UNUSED(enable); + qWarning() << "GL_TEXTURE_CUBE_MAP_SEAMLESS not supported by OpenGL 2.0 (since 3.2)"; +} + +QSize GraphicsHelperGL2::getRenderBufferDimensions(GLuint renderBufferId) +{ + Q_UNUSED(renderBufferId); + qCritical() << "RenderBuffer dimensions retrival not supported on OpenGL 2.0"; + return QSize(0,0); +} + +QSize GraphicsHelperGL2::getTextureDimensions(GLuint textureId, GLenum target, uint level) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindTexture(target, textureId); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height); + m_funcs->glBindTexture(target, 0); + + return QSize(width, height); +} + +void GraphicsHelperGL2::dispatchCompute(GLuint wx, GLuint wy, GLuint wz) +{ + Q_UNUSED(wx); + Q_UNUSED(wy); + Q_UNUSED(wz); + qWarning() << "Compute Shaders are not supported by OpenGL 2.0 (since OpenGL 4.3)"; +} + +char *GraphicsHelperGL2::mapBuffer(GLenum target, GLsizeiptr size) +{ + Q_UNUSED(size); + return static_cast<char*>(m_funcs->glMapBuffer(target, GL_READ_WRITE)); +} + +GLboolean GraphicsHelperGL2::unmapBuffer(GLenum target) +{ + return m_funcs->glUnmapBuffer(target); +} + +void GraphicsHelperGL2::glUniform1fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform1fv(location, count, values); +} + +void GraphicsHelperGL2::glUniform2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform2fv(location, count, values); +} + +void GraphicsHelperGL2::glUniform3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform3fv(location, count, values); +} + +void GraphicsHelperGL2::glUniform4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform4fv(location, count, values); +} + +void GraphicsHelperGL2::glUniform1iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform1iv(location, count, values); +} + +void GraphicsHelperGL2::glUniform2iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform2iv(location, count, values); +} + +void GraphicsHelperGL2::glUniform3iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform3iv(location, count, values); +} + +void GraphicsHelperGL2::glUniform4iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform4iv(location, count, values); +} + +void GraphicsHelperGL2::glUniform1uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform1uiv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniform2uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform2uiv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniform3uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform3uiv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniform4uiv(GLint , GLsizei , const GLuint *) +{ + qWarning() << "glUniform4uiv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2fv(location, count, false, values); +} + +void GraphicsHelperGL2::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3fv(location, count, false, values); +} + +void GraphicsHelperGL2::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4fv(location, count, false, values); +} + +void GraphicsHelperGL2::glUniformMatrix2x3fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix2x3fv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniformMatrix3x2fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix3x2fv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniformMatrix2x4fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix2x4fv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniformMatrix4x2fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix4x2fv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniformMatrix3x4fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix3x4fv not supported by GL 2"; +} + +void GraphicsHelperGL2::glUniformMatrix4x3fv(GLint , GLsizei , const GLfloat *) +{ + qWarning() << "glUniformMatrix4x3fv not supported by GL 2"; +} + +UniformType GraphicsHelperGL2::uniformTypeFromGLType(GLenum type) +{ + switch (type) { + case GL_FLOAT: + return UniformType::Float; + case GL_FLOAT_VEC2: + return UniformType::Vec2; + case GL_FLOAT_VEC3: + return UniformType::Vec3; + case GL_FLOAT_VEC4: + return UniformType::Vec4; + case GL_FLOAT_MAT2: + return UniformType::Mat2; + case GL_FLOAT_MAT3: + return UniformType::Mat3; + case GL_FLOAT_MAT4: + return UniformType::Mat4; + case GL_INT: + return UniformType::Int; + case GL_INT_VEC2: + return UniformType::IVec2; + case GL_INT_VEC3: + return UniformType::IVec3; + case GL_INT_VEC4: + return UniformType::IVec4; + case GL_BOOL: + return UniformType::Bool; + case GL_BOOL_VEC2: + return UniformType::BVec2; + case GL_BOOL_VEC3: + return UniformType::BVec3; + case GL_BOOL_VEC4: + return UniformType::BVec4; + + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_3D: + return UniformType::Sampler; + + default: + Q_UNREACHABLE(); + return UniformType::Float; + } +} + +void GraphicsHelperGL2::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) +{ + Q_UNUSED(srcX0); + Q_UNUSED(srcX1); + Q_UNUSED(srcY0); + Q_UNUSED(srcY1); + Q_UNUSED(dstX0); + Q_UNUSED(dstX1); + Q_UNUSED(dstY0); + Q_UNUSED(dstY1); + Q_UNUSED(mask); + Q_UNUSED(filter); + qWarning() << "Framebuffer blits are not supported by ES 2.0 (since ES 3.1)"; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h new file mode 100644 index 000000000..2db75004f --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL2_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERGL2_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/graphicshelperinterface_p.h> + +#ifndef QT_OPENGL_ES_2 + +QT_BEGIN_NAMESPACE + +class QOpenGLFunctions_2_0; +class QOpenGLExtension_ARB_framebuffer_object; + +namespace Qt3DRender { +namespace Render { + +class Q_AUTOTEST_EXPORT GraphicsHelperGL2 : public GraphicsHelperInterface +{ +public: + GraphicsHelperGL2(); + + // QGraphicHelperInterface interface + void alphaTest(GLenum mode1, GLenum mode2) override; + void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override; + void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override; + void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override; + void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override; + void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override; + void blendEquation(GLenum mode) override; + void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override; + void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override; + void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override; + GLuint boundFrameBufferObject() override; + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override; + bool checkFrameBufferComplete() override; + void clearBufferf(GLint drawbuffer, const QVector4D &values) override; + GLuint createFrameBufferObject() override; + void depthMask(GLenum mode) override; + void depthTest(GLenum mode) override; + void disableClipPlane(int clipPlane) override; + void disablei(GLenum cap, GLuint index) override; + void disablePrimitiveRestart() override; + void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override; + char *mapBuffer(GLenum target, GLsizeiptr size) override; + GLboolean unmapBuffer(GLenum target) override; + void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override; + void drawArraysIndirect(GLenum mode,void *indirect) override; + void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override; + void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override; + void drawBuffers(GLsizei n, const int *bufs) override; + void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override; + void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override; + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override; + void enableClipPlane(int clipPlane) override; + void enablei(GLenum cap, GLuint index) override; + void enablePrimitiveRestart(int primitiveRestartIndex) override; + void enableVertexAttributeArray(int location) override; + void frontFace(GLenum mode) override; + QSize getRenderBufferDimensions(GLuint renderBufferId) override; + QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override; + void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override; + void pointSize(bool programmable, GLfloat value) override; + GLint maxClipPlaneCount() override; + void memoryBarrier(QMemoryBarrier::Operations barriers) override; + QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override; + QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override; + QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override; + QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override; + void releaseFrameBufferObject(GLuint frameBufferId) override; + void setMSAAEnabled(bool enable) override; + void setAlphaCoverageEnabled(bool enable) override; + void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override; + void setSeamlessCubemap(bool enable) override; + void setVerticesPerPatch(GLint verticesPerPatch) override; + bool supportsFeature(Feature feature) const override; + uint uniformByteSize(const ShaderUniform &description) override; + void useProgram(GLuint programId) override; + void vertexAttribDivisor(GLuint index, GLuint divisor) override; + void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override; + void readBuffer(GLenum mode) override; + void drawBuffer(GLenum mode) override; + + void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override; + + void glUniform1iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform2iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform3iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform4iv(GLint location, GLsizei count, const GLint *value) override; + + void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override; + + void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override; + + UniformType uniformTypeFromGLType(GLenum glType) override; + +private: + QOpenGLFunctions_2_0 *m_funcs; + QOpenGLExtension_ARB_framebuffer_object *m_fboFuncs; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL2_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp new file mode 100644 index 000000000..a35c4e37f --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp @@ -0,0 +1,1188 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicshelpergl3_2_p.h" + +#ifndef QT_OPENGL_ES_2 +#include <QOpenGLFunctions_3_2_Core> +#include <QOpenGLFunctions_3_3_Core> +#include <QtOpenGLExtensions/qopenglextensions.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <private/attachmentpack_p.h> +#include <private/qgraphicsutils_p.h> + +QT_BEGIN_NAMESPACE + +# ifndef QT_OPENGL_3 +# define GL_PATCH_VERTICES 36466 +# define GL_ACTIVE_RESOURCES 0x92F5 +# define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +# define GL_BUFFER_BINDING 0x9302 +# define GL_BUFFER_DATA_SIZE 0x9303 +# define GL_NUM_ACTIVE_VARIABLES 0x9304 +# define GL_SHADER_STORAGE_BLOCK 0x92E6 +# define GL_UNIFORM 0x92E1 +# define GL_UNIFORM_BLOCK 0x92E2 +# define GL_UNIFORM_BLOCK_INDEX 0x8A3A +# define GL_UNIFORM_OFFSET 0x8A3B +# define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +# define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +# define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +# define GL_UNIFORM_BLOCK_BINDING 0x8A3F +# define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +# endif + +namespace Qt3DRender { +namespace Render { + +GraphicsHelperGL3_2::GraphicsHelperGL3_2() + : m_funcs(nullptr) + , m_tessFuncs() +{ +} + +GraphicsHelperGL3_2::~GraphicsHelperGL3_2() +{ +} + +void GraphicsHelperGL3_2::initializeHelper(QOpenGLContext *context, + QAbstractOpenGLFunctions *functions) +{ + m_funcs = static_cast<QOpenGLFunctions_3_2_Core*>(functions); + const bool ok = m_funcs->initializeOpenGLFunctions(); + Q_ASSERT(ok); + Q_UNUSED(ok); + + if (context->hasExtension(QByteArrayLiteral("GL_ARB_tessellation_shader"))) { + m_tessFuncs.reset(new QOpenGLExtension_ARB_tessellation_shader); + m_tessFuncs->initializeOpenGLFunctions(); + } +} + +void GraphicsHelperGL3_2::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLsizei instances, + GLint baseVertex, + GLint baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2"; + + // glDrawElements OpenGL 3.1 or greater + m_funcs->glDrawElementsInstancedBaseVertex(primitiveType, + primitiveCount, + indexType, + indices, + instances, + baseVertex); +} + +void GraphicsHelperGL3_2::drawArraysInstanced(GLenum primitiveType, + GLint first, + GLsizei count, + GLsizei instances) +{ + // glDrawArraysInstanced OpenGL 3.1 or greater + m_funcs->glDrawArraysInstanced(primitiveType, + first, + count, + instances); +} + +void GraphicsHelperGL3_2::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL 3"; + m_funcs->glDrawArraysInstanced(primitiveType, + first, + count, + instances); +} + +void GraphicsHelperGL3_2::drawElements(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLint baseVertex) +{ + m_funcs->glDrawElementsBaseVertex(primitiveType, + primitiveCount, + indexType, + indices, + baseVertex); +} + +void GraphicsHelperGL3_2::drawArrays(GLenum primitiveType, + GLint first, + GLsizei count) +{ + m_funcs->glDrawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperGL3_2::drawElementsIndirect(GLenum, GLenum, void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL 3.2"; +} + +void GraphicsHelperGL3_2::drawArraysIndirect(GLenum , void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL 3.2"; +} + +void GraphicsHelperGL3_2::setVerticesPerPatch(GLint verticesPerPatch) +{ +#if defined(QT_OPENGL_4) + if (!m_tessFuncs) { + qWarning() << "Tessellation not supported with OpenGL 3 without GL_ARB_tessellation_shader"; + return; + } + + m_tessFuncs->glPatchParameteri(GL_PATCH_VERTICES, verticesPerPatch); +#else + Q_UNUSED(verticesPerPatch); + qWarning() << "Tessellation not supported"; +#endif +} + +void GraphicsHelperGL3_2::useProgram(GLuint programId) +{ + m_funcs->glUseProgram(programId); +} + +QVector<ShaderUniform> GraphicsHelperGL3_2::programUniformsAndLocations(GLuint programId) +{ + QVector<ShaderUniform> uniforms; + + GLint nbrActiveUniforms = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms); + uniforms.reserve(nbrActiveUniforms); + char uniformName[256]; + for (GLint i = 0; i < nbrActiveUniforms; i++) { + ShaderUniform uniform; + GLsizei uniformNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength, + &uniform.m_size, &uniform.m_type, uniformName); + uniformName[sizeof(uniformName) - 1] = '\0'; + uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName); + uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength); + // Work around for uniform array names that aren't returned with [0] by some drivers + if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]"))) + uniform.m_name.append(QLatin1String("[0]")); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride); + uniform.m_rawByteSize = uniformByteSize(uniform); + uniforms.append(uniform); + qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size + << " offset" << uniform.m_offset + << " rawSize" << uniform.m_rawByteSize; + } + + return uniforms; +} + +QVector<ShaderAttribute> GraphicsHelperGL3_2::programAttributesAndLocations(GLuint programId) +{ + QVector<ShaderAttribute> attributes; + GLint nbrActiveAttributes = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes); + attributes.reserve(nbrActiveAttributes); + char attributeName[256]; + for (GLint i = 0; i < nbrActiveAttributes; i++) { + ShaderAttribute attribute; + GLsizei attributeNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength, + &attribute.m_size, &attribute.m_type, attributeName); + attributeName[sizeof(attributeName) - 1] = '\0'; + attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName); + attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength); + attributes.append(attribute); + } + return attributes; +} + +QVector<ShaderUniformBlock> GraphicsHelperGL3_2::programUniformBlocks(GLuint programId) +{ + QVector<ShaderUniformBlock> blocks; + GLint nbrActiveUniformsBlocks = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORM_BLOCKS, &nbrActiveUniformsBlocks); + blocks.reserve(nbrActiveUniformsBlocks); + for (GLint i = 0; i < nbrActiveUniformsBlocks; i++) { + QByteArray uniformBlockName(256, '\0'); + GLsizei length = 0; + ShaderUniformBlock uniformBlock; + m_funcs->glGetActiveUniformBlockName(programId, i, 256, &length, uniformBlockName.data()); + uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length)); + uniformBlock.m_index = i; + m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformBlock.m_activeUniformsCount); + m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_BINDING, &uniformBlock.m_binding); + m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_DATA_SIZE, &uniformBlock.m_size); + blocks.append(uniformBlock); + } + return blocks; +} + +QVector<ShaderStorageBlock> GraphicsHelperGL3_2::programShaderStorageBlocks(GLuint programId) +{ + Q_UNUSED(programId); + QVector<ShaderStorageBlock> blocks; + qWarning() << "SSBO are not supported by OpenGL 3.2 (since OpenGL 4.3)"; + return blocks; +} + +void GraphicsHelperGL3_2::vertexAttribDivisor(GLuint index, GLuint divisor) +{ + Q_UNUSED(index); + Q_UNUSED(divisor); + qCWarning(Render::Rendering) << "Vertex attribute divisor not available with OpenGL 3.2 core"; +} + +void GraphicsHelperGL3_2::vertexAttributePointer(GLenum shaderDataType, + GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer) +{ + switch (shaderDataType) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + case GL_FLOAT_MAT4: + m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer); + break; + + case GL_INT: + case GL_INT_VEC2: + case GL_INT_VEC3: + case GL_INT_VEC4: + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_VEC2: + case GL_UNSIGNED_INT_VEC3: + case GL_UNSIGNED_INT_VEC4: + m_funcs->glVertexAttribIPointer(index, size, type, stride, pointer); + break; + + default: + qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type"; + Q_UNREACHABLE(); + } +} + +void GraphicsHelperGL3_2::readBuffer(GLenum mode) +{ + m_funcs->glReadBuffer(mode); +} + +void GraphicsHelperGL3_2::drawBuffer(GLenum mode) +{ + m_funcs->glDrawBuffer(mode); +} + +void GraphicsHelperGL3_2::blendEquation(GLenum mode) +{ + m_funcs->glBlendEquation(mode); +} + +void GraphicsHelperGL3_2::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) +{ + Q_UNUSED(buf); + Q_UNUSED(sfactor); + Q_UNUSED(dfactor); + + qWarning() << "glBlendFunci() not supported by OpenGL 3.0 (since OpenGL 4.0)"; +} + +void GraphicsHelperGL3_2::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) +{ + Q_UNUSED(buf); + Q_UNUSED(sRGB); + Q_UNUSED(dRGB); + Q_UNUSED(sAlpha); + Q_UNUSED(dAlpha); + + qWarning() << "glBlendFuncSeparatei() not supported by OpenGL 3.0 (since OpenGL 4.0)"; +} + +void GraphicsHelperGL3_2::alphaTest(GLenum, GLenum) +{ + qCWarning(Render::Rendering) << "AlphaTest not available with OpenGL 3.2 core"; +} + +void GraphicsHelperGL3_2::depthTest(GLenum mode) +{ + m_funcs->glEnable(GL_DEPTH_TEST); + m_funcs->glDepthFunc(mode); +} + +void GraphicsHelperGL3_2::depthMask(GLenum mode) +{ + m_funcs->glDepthMask(mode); +} + +void GraphicsHelperGL3_2::frontFace(GLenum mode) +{ + m_funcs->glFrontFace(mode); + +} + +void GraphicsHelperGL3_2::setMSAAEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_MULTISAMPLE) + : m_funcs->glDisable(GL_MULTISAMPLE); +} + +void GraphicsHelperGL3_2::setAlphaCoverageEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE) + : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); +} + +GLuint GraphicsHelperGL3_2::createFrameBufferObject() +{ + GLuint id; + m_funcs->glGenFramebuffers(1, &id); + return id; +} + +void GraphicsHelperGL3_2::releaseFrameBufferObject(GLuint frameBufferId) +{ + m_funcs->glDeleteFramebuffers(1, &frameBufferId); +} + +void GraphicsHelperGL3_2::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) +{ + switch (mode) { + case FBODraw: + m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId); + return; + case FBORead: + m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId); + return; + case FBOReadAndDraw: + default: + m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId); + return; + } +} + +GLuint GraphicsHelperGL3_2::boundFrameBufferObject() +{ + GLint id = 0; + m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id); + return id; +} + +bool GraphicsHelperGL3_2::checkFrameBufferComplete() +{ + return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +} + +bool GraphicsHelperGL3_2::frameBufferNeedsRenderBuffer(const Attachment &attachment) +{ + Q_UNUSED(attachment); + return false; +} + +void GraphicsHelperGL3_2::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) +{ + GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT; + + if (attachment.m_point <= QRenderTargetOutput::Color15) + attr = GL_COLOR_ATTACHMENT0 + attachment.m_point; + else if (attachment.m_point == QRenderTargetOutput::Depth) + attr = GL_DEPTH_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::Stencil) + attr = GL_STENCIL_ATTACHMENT; + + texture->bind(); + QOpenGLTexture::Target target = texture->target(); + if (target == QOpenGLTexture::Target1DArray || target == QOpenGLTexture::Target2DArray || + target == QOpenGLTexture::Target2DMultisampleArray || target == QOpenGLTexture::Target3D) + m_funcs->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel, attachment.m_layer); + else if (target == QOpenGLTexture::TargetCubeMapArray) + m_funcs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel, attachment.m_layer); + else if (target == QOpenGLTexture::TargetCubeMap) + m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel); + else + m_funcs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel); + texture->release(); +} + +void GraphicsHelperGL3_2::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) +{ + Q_UNUSED(renderBuffer); + Q_UNUSED(attachment); + Q_UNREACHABLE(); +} + +bool GraphicsHelperGL3_2::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case MRT: + case UniformBufferObject: + case PrimitiveRestart: + case RenderBufferDimensionRetrieval: + case TextureDimensionRetrieval: + case BindableFragmentOutputs: + case BlitFramebuffer: + return true; + case Tessellation: + return !m_tessFuncs.isNull(); + default: + return false; + } +} + +void GraphicsHelperGL3_2::drawBuffers(GLsizei n, const int *bufs) +{ + // Use QVarLengthArray here + QVarLengthArray<GLenum, 16> drawBufs(n); + + for (int i = 0; i < n; i++) + drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i]; + m_funcs->glDrawBuffers(n, drawBufs.constData()); +} + +void GraphicsHelperGL3_2::bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) +{ + for (auto it = outputs.begin(), end = outputs.end(); it != end; ++it) + m_funcs->glBindFragDataLocation(shader, it.value(), it.key().toStdString().c_str()); +} + +void GraphicsHelperGL3_2::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +{ + m_funcs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding); +} + +void GraphicsHelperGL3_2::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) +{ + Q_UNUSED(programId); + Q_UNUSED(shaderStorageBlockIndex); + Q_UNUSED(shaderStorageBlockBinding); + qWarning() << "SSBO are not supported by OpenGL 3.0 (since OpenGL 4.3)"; +} + +void GraphicsHelperGL3_2::bindBufferBase(GLenum target, GLuint index, GLuint buffer) +{ + m_funcs->glBindBufferBase(target, index, buffer); +} + +void GraphicsHelperGL3_2::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + char *bufferData = buffer.data(); + + switch (description.m_type) { + + case GL_FLOAT: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_FLOAT_VEC2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_FLOAT_VEC3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_FLOAT_VEC4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_FLOAT_MAT2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2); + break; + } + + case GL_FLOAT_MAT2x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3); + break; + } + + case GL_FLOAT_MAT2x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4); + break; + } + + case GL_FLOAT_MAT3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3); + break; + } + + case GL_FLOAT_MAT3x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2); + break; + } + + case GL_FLOAT_MAT3x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4); + break; + } + + case GL_FLOAT_MAT4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4); + break; + } + + case GL_FLOAT_MAT4x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2); + break; + } + + case GL_FLOAT_MAT4x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3); + break; + } + + case GL_INT: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_INT_VEC2: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_INT_VEC3: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_INT_VEC4: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_UNSIGNED_INT: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_UNSIGNED_INT_VEC2: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_UNSIGNED_INT_VEC3: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_UNSIGNED_INT_VEC4: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_BOOL: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_BOOL_VEC2: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_BOOL_VEC3: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_BOOL_VEC4: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: { + Q_ASSERT(description.m_size == 1); + int value = v.toInt(); + QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1); + break; + } + + default: + qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name; + break; + } +} + +uint GraphicsHelperGL3_2::uniformByteSize(const ShaderUniform &description) +{ + uint rawByteSize = 0; + int arrayStride = qMax(description.m_arrayStride, 0); + int matrixStride = qMax(description.m_matrixStride, 0); + + switch (description.m_type) { + + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_UNSIGNED_INT_VEC2: + rawByteSize = 8; + break; + + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + case GL_UNSIGNED_INT_VEC3: + rawByteSize = 12; + break; + + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + case GL_UNSIGNED_INT_VEC4: + rawByteSize = 16; + break; + + case GL_FLOAT_MAT2: + rawByteSize = matrixStride ? 2 * matrixStride : 16; + break; + + case GL_FLOAT_MAT2x4: + rawByteSize = matrixStride ? 2 * matrixStride : 32; + break; + + case GL_FLOAT_MAT4x2: + rawByteSize = matrixStride ? 4 * matrixStride : 32; + break; + + case GL_FLOAT_MAT3: + rawByteSize = matrixStride ? 3 * matrixStride : 36; + break; + + case GL_FLOAT_MAT2x3: + rawByteSize = matrixStride ? 2 * matrixStride : 24; + break; + + case GL_FLOAT_MAT3x2: + rawByteSize = matrixStride ? 3 * matrixStride : 24; + break; + + case GL_FLOAT_MAT4: + rawByteSize = matrixStride ? 4 * matrixStride : 64; + break; + + case GL_FLOAT_MAT4x3: + rawByteSize = matrixStride ? 4 * matrixStride : 48; + break; + + case GL_FLOAT_MAT3x4: + rawByteSize = matrixStride ? 3 * matrixStride : 48; + break; + + case GL_BOOL: + rawByteSize = 1; + break; + + case GL_BOOL_VEC2: + rawByteSize = 2; + break; + + case GL_BOOL_VEC3: + rawByteSize = 3; + break; + + case GL_BOOL_VEC4: + rawByteSize = 4; + break; + + case GL_INT: + case GL_FLOAT: + case GL_UNSIGNED_INT: + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + rawByteSize = 4; + break; + } + + return arrayStride ? rawByteSize * arrayStride : rawByteSize; +} + +void GraphicsHelperGL3_2::enableClipPlane(int clipPlane) +{ + m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL3_2::disableClipPlane(int clipPlane) +{ + m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL3_2::setClipPlane(int clipPlane, const QVector3D &normal, float distance) +{ + // deprecated + Q_UNUSED(clipPlane); + Q_UNUSED(normal); + Q_UNUSED(distance); +} + +GLint GraphicsHelperGL3_2::maxClipPlaneCount() +{ + GLint max = 0; + m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max); + return max; +} + +void GraphicsHelperGL3_2::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + Q_UNUSED(barriers); + qWarning() << "memory barrier is not supported by OpenGL 3.0 (since 4.3)"; +} + +void GraphicsHelperGL3_2::enablePrimitiveRestart(int primitiveRestartIndex) +{ + m_funcs->glPrimitiveRestartIndex(primitiveRestartIndex); + m_funcs->glEnable(GL_PRIMITIVE_RESTART); +} + +void GraphicsHelperGL3_2::enableVertexAttributeArray(int location) +{ + m_funcs->glEnableVertexAttribArray(location); +} + +void GraphicsHelperGL3_2::disablePrimitiveRestart() +{ + m_funcs->glDisable(GL_PRIMITIVE_RESTART); +} + +void GraphicsHelperGL3_2::clearBufferf(GLint drawbuffer, const QVector4D &values) +{ + GLfloat vec[4] = {values[0], values[1], values[2], values[3]}; + m_funcs->glClearBufferfv(GL_COLOR, drawbuffer, vec); +} + +void GraphicsHelperGL3_2::pointSize(bool programmable, GLfloat value) +{ + if (programmable) { + m_funcs->glEnable(GL_PROGRAM_POINT_SIZE); + } else { + m_funcs->glDisable(GL_PROGRAM_POINT_SIZE); + m_funcs->glPointSize(value); + } +} + +void GraphicsHelperGL3_2::enablei(GLenum cap, GLuint index) +{ + m_funcs->glEnablei(cap, index); +} + +void GraphicsHelperGL3_2::disablei(GLenum cap, GLuint index) +{ + m_funcs->glDisablei(cap, index); +} + +void GraphicsHelperGL3_2::setSeamlessCubemap(bool enable) +{ + if (enable) + m_funcs->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + else + m_funcs->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS); +} + +QSize GraphicsHelperGL3_2::getRenderBufferDimensions(GLuint renderBufferId) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0); + + return QSize(width, height); +} + +QSize GraphicsHelperGL3_2::getTextureDimensions(GLuint textureId, GLenum target, uint level) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindTexture(target, textureId); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height); + m_funcs->glBindTexture(target, 0); + + return QSize(width, height); +} + +void GraphicsHelperGL3_2::dispatchCompute(GLuint wx, GLuint wy, GLuint wz) +{ + Q_UNUSED(wx); + Q_UNUSED(wy); + Q_UNUSED(wz); + qWarning() << "Compute Shaders are not supported by OpenGL 3.2 (since OpenGL 4.3)"; +} + +char *GraphicsHelperGL3_2::mapBuffer(GLenum target, GLsizeiptr size) +{ + return static_cast<char*>(m_funcs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); +} + +GLboolean GraphicsHelperGL3_2::unmapBuffer(GLenum target) +{ + return m_funcs->glUnmapBuffer(target); +} + +void GraphicsHelperGL3_2::glUniform1fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform1fv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform2fv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform3fv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform4fv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform1iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform1iv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform2iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform2iv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform3iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform3iv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform4iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform4iv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform1uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform1uiv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform2uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform2uiv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform3uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform3uiv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniform4uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform4uiv(location, count, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2x3fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3x2fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2x4fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4x2fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3x4fv(location, count, false, values); +} + +void GraphicsHelperGL3_2::glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4x3fv(location, count, false, values); +} + +UniformType GraphicsHelperGL3_2::uniformTypeFromGLType(GLenum type) +{ + switch (type) { + case GL_FLOAT: + return UniformType::Float; + case GL_FLOAT_VEC2: + return UniformType::Vec2; + case GL_FLOAT_VEC3: + return UniformType::Vec3; + case GL_FLOAT_VEC4: + return UniformType::Vec4; + case GL_FLOAT_MAT2: + return UniformType::Mat2; + case GL_FLOAT_MAT3: + return UniformType::Mat3; + case GL_FLOAT_MAT4: + return UniformType::Mat4; + case GL_FLOAT_MAT2x3: + return UniformType::Mat2x3; + case GL_FLOAT_MAT3x2: + return UniformType::Mat3x2; + case GL_FLOAT_MAT2x4: + return UniformType::Mat2x4; + case GL_FLOAT_MAT4x2: + return UniformType::Mat4x2; + case GL_FLOAT_MAT3x4: + return UniformType::Mat3x4; + case GL_FLOAT_MAT4x3: + return UniformType::Mat4x3; + case GL_INT: + return UniformType::Int; + case GL_INT_VEC2: + return UniformType::IVec2; + case GL_INT_VEC3: + return UniformType::IVec3; + case GL_INT_VEC4: + return UniformType::IVec4; + case GL_UNSIGNED_INT: + return UniformType::UInt; + case GL_UNSIGNED_INT_VEC2: + return UniformType::UIVec2; + case GL_UNSIGNED_INT_VEC3: + return UniformType::UIVec3; + case GL_UNSIGNED_INT_VEC4: + return UniformType::UIVec4; + case GL_BOOL: + return UniformType::Bool; + case GL_BOOL_VEC2: + return UniformType::BVec2; + case GL_BOOL_VEC3: + return UniformType::BVec3; + case GL_BOOL_VEC4: + return UniformType::BVec4; + + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_RECT: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_3D: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return UniformType::Sampler; + default: + Q_UNREACHABLE(); + return UniformType::Float; + } +} + +void GraphicsHelperGL3_2::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) +{ + m_funcs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h new file mode 100644 index 000000000..133295fd7 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL3_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERGL3_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <QtCore/qscopedpointer.h> + +#ifndef QT_OPENGL_ES_2 + +QT_BEGIN_NAMESPACE + +class QOpenGLFunctions_3_2_Core; +class QOpenGLExtension_ARB_tessellation_shader; + +namespace Qt3DRender { +namespace Render { + +class Q_AUTOTEST_EXPORT GraphicsHelperGL3_2 : public GraphicsHelperInterface +{ +public: + GraphicsHelperGL3_2(); + ~GraphicsHelperGL3_2(); + + // QGraphicHelperInterface interface + void alphaTest(GLenum mode1, GLenum mode2) override; + void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override; + void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override; + void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override; + void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override; + void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override; + void blendEquation(GLenum mode) override; + void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override; + void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override; + void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override; + GLuint boundFrameBufferObject() override; + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override; + bool checkFrameBufferComplete() override; + void clearBufferf(GLint drawbuffer, const QVector4D &values) override; + GLuint createFrameBufferObject() override; + void depthMask(GLenum mode) override; + void depthTest(GLenum mode) override; + void disableClipPlane(int clipPlane) override; + void disablei(GLenum cap, GLuint index) override; + void disablePrimitiveRestart() override; + void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override; + char *mapBuffer(GLenum target, GLsizeiptr size) override; + GLboolean unmapBuffer(GLenum target) override; + void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override; + void drawArraysIndirect(GLenum mode,void *indirect) override; + void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override; + void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override; + void drawBuffers(GLsizei n, const int *bufs) override; + void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override; + void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override; + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override; + void enableClipPlane(int clipPlane) override; + void enablei(GLenum cap, GLuint index) override; + void enablePrimitiveRestart(int primitiveRestartIndex) override; + void enableVertexAttributeArray(int location) override; + void frontFace(GLenum mode) override; + QSize getRenderBufferDimensions(GLuint renderBufferId) override; + QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override; + void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override; + void pointSize(bool programmable, GLfloat value) override; + GLint maxClipPlaneCount() override; + void memoryBarrier(QMemoryBarrier::Operations barriers) override; + QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override; + QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override; + QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override; + QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override; + void releaseFrameBufferObject(GLuint frameBufferId) override; + void setMSAAEnabled(bool enable) override; + void setAlphaCoverageEnabled(bool enable) override; + void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override; + void setSeamlessCubemap(bool enable) override; + void setVerticesPerPatch(GLint verticesPerPatch) override; + bool supportsFeature(Feature feature) const override; + uint uniformByteSize(const ShaderUniform &description) override; + void useProgram(GLuint programId) override; + void vertexAttribDivisor(GLuint index, GLuint divisor) override; + void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override; + void readBuffer(GLenum mode) override; + void drawBuffer(GLenum mode) override; + + void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override; + + void glUniform1iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform2iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform3iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform4iv(GLint location, GLsizei count, const GLint *value) override; + + void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override; + + void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override; + + UniformType uniformTypeFromGLType(GLenum glType) override; + +private: + QOpenGLFunctions_3_2_Core *m_funcs; + QScopedPointer<QOpenGLExtension_ARB_tessellation_shader> m_tessFuncs; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL3_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp new file mode 100644 index 000000000..b2512d84a --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp @@ -0,0 +1,1184 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicshelpergl3_3_p.h" + +#ifndef QT_OPENGL_ES_2 +#include <QOpenGLFunctions_3_3_Core> +#include <QtOpenGLExtensions/qopenglextensions.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <private/attachmentpack_p.h> +#include <private/qgraphicsutils_p.h> + +# ifndef QT_OPENGL_3_2 +# define GL_PATCH_VERTICES 36466 +# define GL_ACTIVE_RESOURCES 0x92F5 +# define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +# define GL_BUFFER_BINDING 0x9302 +# define GL_BUFFER_DATA_SIZE 0x9303 +# define GL_NUM_ACTIVE_VARIABLES 0x9304 +# define GL_SHADER_STORAGE_BLOCK 0x92E6 +# define GL_UNIFORM 0x92E1 +# define GL_UNIFORM_BLOCK 0x92E2 +# define GL_UNIFORM_BLOCK_INDEX 0x8A3A +# define GL_UNIFORM_OFFSET 0x8A3B +# define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +# define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +# define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +# define GL_UNIFORM_BLOCK_BINDING 0x8A3F +# define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +# endif + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +GraphicsHelperGL3_3::GraphicsHelperGL3_3() + : m_funcs(nullptr) + , m_tessFuncs() +{ +} + +GraphicsHelperGL3_3::~GraphicsHelperGL3_3() +{ +} + +void GraphicsHelperGL3_3::initializeHelper(QOpenGLContext *context, + QAbstractOpenGLFunctions *functions) +{ + m_funcs = static_cast<QOpenGLFunctions_3_3_Core*>(functions); + const bool ok = m_funcs->initializeOpenGLFunctions(); + Q_ASSERT(ok); + Q_UNUSED(ok); + + if (context->hasExtension(QByteArrayLiteral("GL_ARB_tessellation_shader"))) { + m_tessFuncs.reset(new QOpenGLExtension_ARB_tessellation_shader); + m_tessFuncs->initializeOpenGLFunctions(); + } +} + +void GraphicsHelperGL3_3::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLsizei instances, + GLint baseVertex, + GLint baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL 3"; + + // glDrawElements OpenGL 3.1 or greater + m_funcs->glDrawElementsInstancedBaseVertex(primitiveType, + primitiveCount, + indexType, + indices, + instances, + baseVertex); +} + +void GraphicsHelperGL3_3::drawArraysInstanced(GLenum primitiveType, + GLint first, + GLsizei count, + GLsizei instances) +{ + // glDrawArraysInstanced OpenGL 3.1 or greater + m_funcs->glDrawArraysInstanced(primitiveType, + first, + count, + instances); +} + +void GraphicsHelperGL3_3::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawArraysInstancedBaseInstance is not supported with OpenGL 3"; + m_funcs->glDrawArraysInstanced(primitiveType, + first, + count, + instances); +} + +void GraphicsHelperGL3_3::drawElements(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLint baseVertex) +{ + m_funcs->glDrawElementsBaseVertex(primitiveType, + primitiveCount, + indexType, + indices, + baseVertex); +} + +void GraphicsHelperGL3_3::drawElementsIndirect(GLenum, GLenum, void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL 3"; +} + +void GraphicsHelperGL3_3::drawArrays(GLenum primitiveType, + GLint first, + GLsizei count) +{ + m_funcs->glDrawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperGL3_3::drawArraysIndirect(GLenum , void *) +{ + qWarning() << "Indirect Drawing is not supported with OpenGL 3"; +} + +void GraphicsHelperGL3_3::setVerticesPerPatch(GLint verticesPerPatch) +{ +#if defined(QT_OPENGL_4) + if (!m_tessFuncs) { + qWarning() << "Tessellation not supported with OpenGL 3 without GL_ARB_tessellation_shader"; + return; + } + + m_tessFuncs->glPatchParameteri(GL_PATCH_VERTICES, verticesPerPatch); +#else + Q_UNUSED(verticesPerPatch); + qWarning() << "Tessellation not supported"; +#endif +} + +void GraphicsHelperGL3_3::useProgram(GLuint programId) +{ + m_funcs->glUseProgram(programId); +} + +QVector<ShaderUniform> GraphicsHelperGL3_3::programUniformsAndLocations(GLuint programId) +{ + QVector<ShaderUniform> uniforms; + + GLint nbrActiveUniforms = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nbrActiveUniforms); + uniforms.reserve(nbrActiveUniforms); + char uniformName[256]; + for (GLint i = 0; i < nbrActiveUniforms; i++) { + ShaderUniform uniform; + GLsizei uniformNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength, + &uniform.m_size, &uniform.m_type, uniformName); + uniformName[sizeof(uniformName) - 1] = '\0'; + uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName); + uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength); + // Work around for uniform array names that aren't returned with [0] by some drivers + if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]"))) + uniform.m_name.append(QLatin1String("[0]")); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride); + uniform.m_rawByteSize = uniformByteSize(uniform); + uniforms.append(uniform); + qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size + << " offset" << uniform.m_offset + << " rawSize" << uniform.m_rawByteSize; + } + + return uniforms; +} + +QVector<ShaderAttribute> GraphicsHelperGL3_3::programAttributesAndLocations(GLuint programId) +{ + QVector<ShaderAttribute> attributes; + GLint nbrActiveAttributes = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes); + attributes.reserve(nbrActiveAttributes); + char attributeName[256]; + for (GLint i = 0; i < nbrActiveAttributes; i++) { + ShaderAttribute attribute; + GLsizei attributeNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength, + &attribute.m_size, &attribute.m_type, attributeName); + attributeName[sizeof(attributeName) - 1] = '\0'; + attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName); + attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength); + attributes.append(attribute); + } + return attributes; +} + +QVector<ShaderUniformBlock> GraphicsHelperGL3_3::programUniformBlocks(GLuint programId) +{ + QVector<ShaderUniformBlock> blocks; + GLint nbrActiveUniformsBlocks = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_UNIFORM_BLOCKS, &nbrActiveUniformsBlocks); + blocks.reserve(nbrActiveUniformsBlocks); + for (GLint i = 0; i < nbrActiveUniformsBlocks; i++) { + QByteArray uniformBlockName(256, '\0'); + GLsizei length = 0; + ShaderUniformBlock uniformBlock; + m_funcs->glGetActiveUniformBlockName(programId, i, 256, &length, uniformBlockName.data()); + uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length)); + uniformBlock.m_index = i; + m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &uniformBlock.m_activeUniformsCount); + m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_BINDING, &uniformBlock.m_binding); + m_funcs->glGetActiveUniformBlockiv(programId, i, GL_UNIFORM_BLOCK_DATA_SIZE, &uniformBlock.m_size); + blocks.append(uniformBlock); + } + return blocks; +} + +QVector<ShaderStorageBlock> GraphicsHelperGL3_3::programShaderStorageBlocks(GLuint programId) +{ + Q_UNUSED(programId); + QVector<ShaderStorageBlock> blocks; + qWarning() << "SSBO are not supported by OpenGL 3.3 (since OpenGL 4.3)"; + return blocks; +} + +void GraphicsHelperGL3_3::vertexAttribDivisor(GLuint index, GLuint divisor) +{ + m_funcs->glVertexAttribDivisor(index, divisor); +} + +void GraphicsHelperGL3_3::vertexAttributePointer(GLenum shaderDataType, + GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer) +{ + switch (shaderDataType) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + case GL_FLOAT_MAT4: + m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer); + break; + + case GL_INT: + case GL_INT_VEC2: + case GL_INT_VEC3: + case GL_INT_VEC4: + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_VEC2: + case GL_UNSIGNED_INT_VEC3: + case GL_UNSIGNED_INT_VEC4: + m_funcs->glVertexAttribIPointer(index, size, type, stride, pointer); + break; + + default: + qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type"; + } +} + +void GraphicsHelperGL3_3::readBuffer(GLenum mode) +{ + m_funcs->glReadBuffer(mode); +} + +void GraphicsHelperGL3_3::drawBuffer(GLenum mode) +{ + m_funcs->glDrawBuffer(mode); +} + +void GraphicsHelperGL3_3::blendEquation(GLenum mode) +{ + m_funcs->glBlendEquation(mode); +} + +void GraphicsHelperGL3_3::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) +{ + Q_UNUSED(buf); + Q_UNUSED(sfactor); + Q_UNUSED(dfactor); + + qWarning() << "glBlendFunci() not supported by OpenGL 3.3 (since OpenGL 4.0)"; +} + +void GraphicsHelperGL3_3::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) +{ + Q_UNUSED(buf); + Q_UNUSED(sRGB); + Q_UNUSED(dRGB); + Q_UNUSED(sAlpha); + Q_UNUSED(dAlpha); + + qWarning() << "glBlendFuncSeparatei() not supported by OpenGL 3.3 (since OpenGL 4.0)"; +} + +void GraphicsHelperGL3_3::alphaTest(GLenum, GLenum) +{ + qCWarning(Render::Rendering) << "AlphaTest not available with OpenGL 3.2 core"; +} + +void GraphicsHelperGL3_3::depthTest(GLenum mode) +{ + m_funcs->glEnable(GL_DEPTH_TEST); + m_funcs->glDepthFunc(mode); +} + +void GraphicsHelperGL3_3::depthMask(GLenum mode) +{ + m_funcs->glDepthMask(mode); +} + +void GraphicsHelperGL3_3::frontFace(GLenum mode) +{ + m_funcs->glFrontFace(mode); + +} + +void GraphicsHelperGL3_3::setMSAAEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_MULTISAMPLE) + : m_funcs->glDisable(GL_MULTISAMPLE); +} + +void GraphicsHelperGL3_3::setAlphaCoverageEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE) + : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); +} + +GLuint GraphicsHelperGL3_3::createFrameBufferObject() +{ + GLuint id; + m_funcs->glGenFramebuffers(1, &id); + return id; +} + +void GraphicsHelperGL3_3::releaseFrameBufferObject(GLuint frameBufferId) +{ + m_funcs->glDeleteFramebuffers(1, &frameBufferId); +} + +void GraphicsHelperGL3_3::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) +{ + switch (mode) { + case FBODraw: + m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId); + return; + case FBORead: + m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId); + return; + case FBOReadAndDraw: + default: + m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId); + return; + } +} + +GLuint GraphicsHelperGL3_3::boundFrameBufferObject() +{ + GLint id = 0; + m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id); + return id; +} + +bool GraphicsHelperGL3_3::checkFrameBufferComplete() +{ + return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +} + +bool GraphicsHelperGL3_3::frameBufferNeedsRenderBuffer(const Attachment &attachment) +{ + Q_UNUSED(attachment); + return false; +} + +void GraphicsHelperGL3_3::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) +{ + GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT; + + if (attachment.m_point <= QRenderTargetOutput::Color15) + attr = GL_COLOR_ATTACHMENT0 + attachment.m_point; + else if (attachment.m_point == QRenderTargetOutput::Depth) + attr = GL_DEPTH_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::Stencil) + attr = GL_STENCIL_ATTACHMENT; + + texture->bind(); + QOpenGLTexture::Target target = texture->target(); + if (target == QOpenGLTexture::Target1DArray || target == QOpenGLTexture::Target2DArray || + target == QOpenGLTexture::Target2DMultisampleArray || target == QOpenGLTexture::Target3D) + m_funcs->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel, attachment.m_layer); + else if (target == QOpenGLTexture::TargetCubeMapArray) + m_funcs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel, attachment.m_layer); + else if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face != QAbstractTexture::AllFaces) + m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel); + else + m_funcs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel); + texture->release(); +} + +void GraphicsHelperGL3_3::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) +{ + Q_UNUSED(renderBuffer); + Q_UNUSED(attachment); + Q_UNREACHABLE(); +} + +bool GraphicsHelperGL3_3::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case MRT: + case UniformBufferObject: + case PrimitiveRestart: + case RenderBufferDimensionRetrieval: + case TextureDimensionRetrieval: + case BindableFragmentOutputs: + case BlitFramebuffer: + return true; + case Tessellation: + return !m_tessFuncs.isNull(); + default: + return false; + } +} + +void GraphicsHelperGL3_3::drawBuffers(GLsizei n, const int *bufs) +{ + // Use QVarLengthArray here + QVarLengthArray<GLenum, 16> drawBufs(n); + + for (int i = 0; i < n; i++) + drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i]; + m_funcs->glDrawBuffers(n, drawBufs.constData()); +} + +void GraphicsHelperGL3_3::bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) +{ + for (auto it = outputs.begin(), end = outputs.end(); it != end; ++it) + m_funcs->glBindFragDataLocation(shader, it.value(), it.key().toStdString().c_str()); +} + +void GraphicsHelperGL3_3::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +{ + m_funcs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding); +} + +void GraphicsHelperGL3_3::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) +{ + Q_UNUSED(programId); + Q_UNUSED(shaderStorageBlockIndex); + Q_UNUSED(shaderStorageBlockBinding); + qWarning() << "SSBO are not supported by OpenGL 3.3 (since OpenGL 4.3)"; +} + +void GraphicsHelperGL3_3::bindBufferBase(GLenum target, GLuint index, GLuint buffer) +{ + m_funcs->glBindBufferBase(target, index, buffer); +} + +void GraphicsHelperGL3_3::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + char *bufferData = buffer.data(); + + switch (description.m_type) { + + case GL_FLOAT: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_FLOAT_VEC2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_FLOAT_VEC3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_FLOAT_VEC4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_FLOAT_MAT2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2); + break; + } + + case GL_FLOAT_MAT2x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3); + break; + } + + case GL_FLOAT_MAT2x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4); + break; + } + + case GL_FLOAT_MAT3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3); + break; + } + + case GL_FLOAT_MAT3x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2); + break; + } + + case GL_FLOAT_MAT3x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4); + break; + } + + case GL_FLOAT_MAT4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4); + break; + } + + case GL_FLOAT_MAT4x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2); + break; + } + + case GL_FLOAT_MAT4x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3); + break; + } + + case GL_INT: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_INT_VEC2: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_INT_VEC3: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_INT_VEC4: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_UNSIGNED_INT: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_UNSIGNED_INT_VEC2: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_UNSIGNED_INT_VEC3: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_UNSIGNED_INT_VEC4: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_BOOL: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_BOOL_VEC2: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_BOOL_VEC3: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_BOOL_VEC4: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: { + Q_ASSERT(description.m_size == 1); + int value = v.toInt(); + QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1); + break; + } + + default: + qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name; + break; + } +} + +uint GraphicsHelperGL3_3::uniformByteSize(const ShaderUniform &description) +{ + uint rawByteSize = 0; + int arrayStride = qMax(description.m_arrayStride, 0); + int matrixStride = qMax(description.m_matrixStride, 0); + + switch (description.m_type) { + + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_UNSIGNED_INT_VEC2: + rawByteSize = 8; + break; + + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + case GL_UNSIGNED_INT_VEC3: + rawByteSize = 12; + break; + + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + case GL_UNSIGNED_INT_VEC4: + rawByteSize = 16; + break; + + case GL_FLOAT_MAT2: + rawByteSize = matrixStride ? 2 * matrixStride : 16; + break; + + case GL_FLOAT_MAT2x4: + rawByteSize = matrixStride ? 2 * matrixStride : 32; + break; + + case GL_FLOAT_MAT4x2: + rawByteSize = matrixStride ? 4 * matrixStride : 32; + break; + + case GL_FLOAT_MAT3: + rawByteSize = matrixStride ? 3 * matrixStride : 36; + break; + + case GL_FLOAT_MAT2x3: + rawByteSize = matrixStride ? 2 * matrixStride : 24; + break; + + case GL_FLOAT_MAT3x2: + rawByteSize = matrixStride ? 3 * matrixStride : 24; + break; + + case GL_FLOAT_MAT4: + rawByteSize = matrixStride ? 4 * matrixStride : 64; + break; + + case GL_FLOAT_MAT4x3: + rawByteSize = matrixStride ? 4 * matrixStride : 48; + break; + + case GL_FLOAT_MAT3x4: + rawByteSize = matrixStride ? 3 * matrixStride : 48; + break; + + case GL_BOOL: + rawByteSize = 1; + break; + + case GL_BOOL_VEC2: + rawByteSize = 2; + break; + + case GL_BOOL_VEC3: + rawByteSize = 3; + break; + + case GL_BOOL_VEC4: + rawByteSize = 4; + break; + + case GL_INT: + case GL_FLOAT: + case GL_UNSIGNED_INT: + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + rawByteSize = 4; + break; + } + + return arrayStride ? rawByteSize * arrayStride : rawByteSize; +} + +void GraphicsHelperGL3_3::enableClipPlane(int clipPlane) +{ + m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL3_3::disableClipPlane(int clipPlane) +{ + m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL3_3::setClipPlane(int clipPlane, const QVector3D &normal, float distance) +{ + // deprecated + Q_UNUSED(clipPlane); + Q_UNUSED(normal); + Q_UNUSED(distance); +} + +GLint GraphicsHelperGL3_3::maxClipPlaneCount() +{ + GLint max = 0; + m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max); + return max; +} + +void GraphicsHelperGL3_3::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + Q_UNUSED(barriers); + qWarning() << "memory barrier is not supported by OpenGL 3.3 (since 4.3)"; +} + +void GraphicsHelperGL3_3::enablePrimitiveRestart(int primitiveRestartIndex) +{ + m_funcs->glPrimitiveRestartIndex(primitiveRestartIndex); + m_funcs->glEnable(GL_PRIMITIVE_RESTART); +} + +void GraphicsHelperGL3_3::enableVertexAttributeArray(int location) +{ + m_funcs->glEnableVertexAttribArray(location); +} + +void GraphicsHelperGL3_3::disablePrimitiveRestart() +{ + m_funcs->glDisable(GL_PRIMITIVE_RESTART); +} + +void GraphicsHelperGL3_3::clearBufferf(GLint drawbuffer, const QVector4D &values) +{ + GLfloat vec[4] = {values[0], values[1], values[2], values[3]}; + m_funcs->glClearBufferfv(GL_COLOR, drawbuffer, vec); +} + +void GraphicsHelperGL3_3::pointSize(bool programmable, GLfloat value) +{ + if (programmable) { + m_funcs->glEnable(GL_PROGRAM_POINT_SIZE); + } else { + m_funcs->glDisable(GL_PROGRAM_POINT_SIZE); + m_funcs->glPointSize(value); + } +} + +void GraphicsHelperGL3_3::enablei(GLenum cap, GLuint index) +{ + m_funcs->glEnablei(cap, index); +} + +void GraphicsHelperGL3_3::disablei(GLenum cap, GLuint index) +{ + m_funcs->glDisablei(cap, index); +} + +void GraphicsHelperGL3_3::setSeamlessCubemap(bool enable) +{ + if (enable) + m_funcs->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + else + m_funcs->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS); +} + +QSize GraphicsHelperGL3_3::getRenderBufferDimensions(GLuint renderBufferId) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0); + + return QSize(width, height); +} + +QSize GraphicsHelperGL3_3::getTextureDimensions(GLuint textureId, GLenum target, uint level) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindTexture(target, textureId); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height); + m_funcs->glBindTexture(target, 0); + + return QSize(width, height); +} + +void GraphicsHelperGL3_3::dispatchCompute(GLuint wx, GLuint wy, GLuint wz) +{ + Q_UNUSED(wx); + Q_UNUSED(wy); + Q_UNUSED(wz); + qWarning() << "Compute Shaders are not supported by OpenGL 3.3 (since OpenGL 4.3)"; +} + +char *GraphicsHelperGL3_3::mapBuffer(GLenum target, GLsizeiptr size) +{ + return static_cast<char*>(m_funcs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); +} + +GLboolean GraphicsHelperGL3_3::unmapBuffer(GLenum target) +{ + return m_funcs->glUnmapBuffer(target); +} + +void GraphicsHelperGL3_3::glUniform1fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform1fv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform2fv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform3fv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform4fv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform1iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform1iv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform2iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform2iv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform3iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform3iv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform4iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform4iv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform1uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform1uiv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform2uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform2uiv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform3uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform3uiv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniform4uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform4uiv(location, count, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2x3fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3x2fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2x4fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4x2fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3x4fv(location, count, false, values); +} + +void GraphicsHelperGL3_3::glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4x3fv(location, count, false, values); +} + +UniformType GraphicsHelperGL3_3::uniformTypeFromGLType(GLenum type) +{ + switch (type) { + case GL_FLOAT: + return UniformType::Float; + case GL_FLOAT_VEC2: + return UniformType::Vec2; + case GL_FLOAT_VEC3: + return UniformType::Vec3; + case GL_FLOAT_VEC4: + return UniformType::Vec4; + case GL_FLOAT_MAT2: + return UniformType::Mat2; + case GL_FLOAT_MAT3: + return UniformType::Mat3; + case GL_FLOAT_MAT4: + return UniformType::Mat4; + case GL_FLOAT_MAT2x3: + return UniformType::Mat2x3; + case GL_FLOAT_MAT3x2: + return UniformType::Mat3x2; + case GL_FLOAT_MAT2x4: + return UniformType::Mat2x4; + case GL_FLOAT_MAT4x2: + return UniformType::Mat4x2; + case GL_FLOAT_MAT3x4: + return UniformType::Mat3x4; + case GL_FLOAT_MAT4x3: + return UniformType::Mat4x3; + case GL_INT: + return UniformType::Int; + case GL_INT_VEC2: + return UniformType::IVec2; + case GL_INT_VEC3: + return UniformType::IVec3; + case GL_INT_VEC4: + return UniformType::IVec4; + case GL_UNSIGNED_INT: + return UniformType::UInt; + case GL_UNSIGNED_INT_VEC2: + return UniformType::UIVec2; + case GL_UNSIGNED_INT_VEC3: + return UniformType::UIVec3; + case GL_UNSIGNED_INT_VEC4: + return UniformType::UIVec4; + case GL_BOOL: + return UniformType::Bool; + case GL_BOOL_VEC2: + return UniformType::BVec2; + case GL_BOOL_VEC3: + return UniformType::BVec3; + case GL_BOOL_VEC4: + return UniformType::BVec4; + + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_RECT: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_3D: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return UniformType::Sampler; + default: + Q_UNREACHABLE(); + return UniformType::Float; + } +} + +void GraphicsHelperGL3_3::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) +{ + m_funcs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h new file mode 100644 index 000000000..0ecdd3620 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL3_3_P_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERGL3_3_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <QtCore/qscopedpointer.h> + +#ifndef QT_OPENGL_ES_2 + +QT_BEGIN_NAMESPACE + +class QOpenGLFunctions_3_3_Core; +class QOpenGLExtension_ARB_tessellation_shader; + +namespace Qt3DRender { +namespace Render { + +class Q_AUTOTEST_EXPORT GraphicsHelperGL3_3 : public GraphicsHelperInterface +{ +public: + GraphicsHelperGL3_3(); + ~GraphicsHelperGL3_3(); + + // QGraphicHelperInterface interface + void alphaTest(GLenum mode1, GLenum mode2) override; + void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override; + void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override; + void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override; + void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override; + void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override; + void blendEquation(GLenum mode) override; + void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override; + void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override; + void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override; + GLuint boundFrameBufferObject() override; + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override; + bool checkFrameBufferComplete() override; + void clearBufferf(GLint drawbuffer, const QVector4D &values) override; + GLuint createFrameBufferObject() override; + void depthMask(GLenum mode) override; + void depthTest(GLenum mode) override; + void disableClipPlane(int clipPlane) override; + void disablei(GLenum cap, GLuint index) override; + void disablePrimitiveRestart() override; + void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override; + char *mapBuffer(GLenum target, GLsizeiptr size) override; + GLboolean unmapBuffer(GLenum target) override; + void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override; + void drawArraysIndirect(GLenum mode,void *indirect) override; + void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override; + void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override; + void drawBuffers(GLsizei n, const int *bufs) override; + void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override; + void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override; + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override; + void enableClipPlane(int clipPlane) override; + void enablei(GLenum cap, GLuint index) override; + void enablePrimitiveRestart(int primitiveRestartIndex) override; + void enableVertexAttributeArray(int location) override; + void frontFace(GLenum mode) override; + QSize getRenderBufferDimensions(GLuint renderBufferId) override; + QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override; + void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override; + void pointSize(bool programmable, GLfloat value) override; + GLint maxClipPlaneCount() override; + void memoryBarrier(QMemoryBarrier::Operations barriers) override; + QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override; + QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override; + QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override; + QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override; + void releaseFrameBufferObject(GLuint frameBufferId) override; + void setMSAAEnabled(bool enable) override; + void setAlphaCoverageEnabled(bool enable) override; + void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override; + void setSeamlessCubemap(bool enable) override; + void setVerticesPerPatch(GLint verticesPerPatch) override; + bool supportsFeature(Feature feature) const override; + uint uniformByteSize(const ShaderUniform &description) override; + void useProgram(GLuint programId) override; + void vertexAttribDivisor(GLuint index, GLuint divisor) override; + void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override; + void readBuffer(GLenum mode) override; + void drawBuffer(GLenum mode) override; + + void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override; + + void glUniform1iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform2iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform3iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform4iv(GLint location, GLsizei count, const GLint *value) override; + + void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override; + + void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override; + + UniformType uniformTypeFromGLType(GLenum glType) override; + +private: + QOpenGLFunctions_3_3_Core *m_funcs; + QScopedPointer<QOpenGLExtension_ARB_tessellation_shader> m_tessFuncs; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL3_3_P_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp new file mode 100644 index 000000000..ce1b8ac2b --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp @@ -0,0 +1,1242 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicshelpergl4_p.h" + +#ifndef QT_OPENGL_ES_2 +#include <QOpenGLFunctions_4_3_Core> +#include <QtOpenGLExtensions/qopenglextensions.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <private/attachmentpack_p.h> +#include <private/qgraphicsutils_p.h> + +# ifndef QT_OPENGL_4_3 +# ifndef GL_PATCH_VERTICES +# define GL_PATCH_VERTICES 36466 +# endif +# define GL_ACTIVE_RESOURCES 0x92F5 +# define GL_BUFFER_BINDING 0x9302 +# define GL_BUFFER_DATA_SIZE 0x9303 +# define GL_NUM_ACTIVE_VARIABLES 0x9304 +# define GL_SHADER_STORAGE_BLOCK 0x92E6 +# define GL_UNIFORM 0x92E1 +# define GL_UNIFORM_BLOCK 0x92E2 +# define GL_UNIFORM_BLOCK_INDEX 0x8A3A +# define GL_UNIFORM_OFFSET 0x8A3B +# define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +# define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +# define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +# define GL_UNIFORM_BLOCK_BINDING 0x8A3F +# define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +# define GL_ALL_BARRIER_BITS 0xFFFFFFFF +# define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +# define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +# define GL_UNIFORM_BARRIER_BIT 0x00000004 +# define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +# define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +# define GL_COMMAND_BARRIER_BIT 0x00000040 +# define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +# define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +# define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +# define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +# define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +# define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +# define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +# define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +# endif + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +namespace { + +GLbitfield memoryBarrierGLBitfield(QMemoryBarrier::Operations barriers) +{ + GLbitfield bits = 0; + + if (barriers.testFlag(QMemoryBarrier::All)) { + bits |= GL_ALL_BARRIER_BITS; + return bits; + } + + if (barriers.testFlag(QMemoryBarrier::VertexAttributeArray)) + bits |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::ElementArray)) + bits |= GL_ELEMENT_ARRAY_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::Uniform)) + bits |= GL_UNIFORM_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::TextureFetch)) + bits |= GL_TEXTURE_FETCH_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::ShaderImageAccess)) + bits |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::Command)) + bits |= GL_COMMAND_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::PixelBuffer)) + bits |= GL_PIXEL_BUFFER_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::TextureUpdate)) + bits |= GL_TEXTURE_UPDATE_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::BufferUpdate)) + bits |= GL_BUFFER_UPDATE_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::FrameBuffer)) + bits |= GL_FRAMEBUFFER_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::TransformFeedback)) + bits |= GL_TRANSFORM_FEEDBACK_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::AtomicCounter)) + bits |= GL_ATOMIC_COUNTER_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::ShaderStorage)) + bits |= GL_SHADER_STORAGE_BARRIER_BIT; + if (barriers.testFlag(QMemoryBarrier::QueryBuffer)) + bits |= GL_QUERY_BUFFER_BARRIER_BIT; + + return bits; +} + +} + +GraphicsHelperGL4::GraphicsHelperGL4() + : m_funcs(nullptr) +{ +} + +void GraphicsHelperGL4::initializeHelper(QOpenGLContext *context, + QAbstractOpenGLFunctions *functions) +{ + Q_UNUSED(context); + m_funcs = static_cast<QOpenGLFunctions_4_3_Core*>(functions); + const bool ok = m_funcs->initializeOpenGLFunctions(); + Q_ASSERT(ok); + Q_UNUSED(ok); +} + +void GraphicsHelperGL4::drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLsizei instances, + GLint baseVertex, + GLint baseInstance) +{ + if (baseInstance != 0) + qWarning() << "glDrawElementsInstancedBaseVertexBaseInstance is not supported with OpenGL ES 2"; + + // glDrawElements OpenGL 3.1 or greater + m_funcs->glDrawElementsInstancedBaseVertex(primitiveType, + primitiveCount, + indexType, + indices, + instances, + baseVertex); +} + +void GraphicsHelperGL4::drawArraysInstanced(GLenum primitiveType, + GLint first, + GLsizei count, + GLsizei instances) +{ + // glDrawArraysInstanced OpenGL 3.1 or greater + m_funcs->glDrawArraysInstanced(primitiveType, + first, + count, + instances); +} + +void GraphicsHelperGL4::drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) +{ + m_funcs->glDrawArraysInstancedBaseInstance(primitiveType, + first, + count, + instances, + baseInstance); +} + +void GraphicsHelperGL4::drawElements(GLenum primitiveType, + GLsizei primitiveCount, + GLint indexType, + void *indices, + GLint baseVertex) +{ + m_funcs->glDrawElementsBaseVertex(primitiveType, + primitiveCount, + indexType, + indices, + baseVertex); +} + +void GraphicsHelperGL4::drawElementsIndirect(GLenum mode, + GLenum type, + void *indirect) +{ + m_funcs->glDrawElementsIndirect(mode, type, indirect); +} + +void GraphicsHelperGL4::drawArrays(GLenum primitiveType, + GLint first, + GLsizei count) +{ + m_funcs->glDrawArrays(primitiveType, + first, + count); +} + +void GraphicsHelperGL4::drawArraysIndirect(GLenum mode, void *indirect) +{ + m_funcs->glDrawArraysIndirect(mode, indirect); +} + +void GraphicsHelperGL4::setVerticesPerPatch(GLint verticesPerPatch) +{ + m_funcs->glPatchParameteri(GL_PATCH_VERTICES, verticesPerPatch); +} + +void GraphicsHelperGL4::useProgram(GLuint programId) +{ + m_funcs->glUseProgram(programId); +} + +QVector<ShaderUniform> GraphicsHelperGL4::programUniformsAndLocations(GLuint programId) +{ + QVector<ShaderUniform> uniforms; + + GLint nbrActiveUniforms = 0; + m_funcs->glGetProgramInterfaceiv(programId, GL_UNIFORM, GL_ACTIVE_RESOURCES, &nbrActiveUniforms); + uniforms.reserve(nbrActiveUniforms); + char uniformName[256]; + for (GLint i = 0; i < nbrActiveUniforms; ++i) { + ShaderUniform uniform; + GLsizei uniformNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveUniform(programId, i, sizeof(uniformName) - 1, &uniformNameLength, + &uniform.m_size, &uniform.m_type, uniformName); + uniformName[sizeof(uniformName) - 1] = '\0'; + uniform.m_location = m_funcs->glGetUniformLocation(programId, uniformName); + uniform.m_name = QString::fromUtf8(uniformName, uniformNameLength); + // Work around for uniform array names that aren't returned with [0] by some drivers + if (uniform.m_size > 1 && !uniform.m_name.endsWith(QLatin1String("[0]"))) + uniform.m_name.append(QLatin1String("[0]")); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &uniform.m_blockIndex); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniform.m_offset); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniform.m_arrayStride); + m_funcs->glGetActiveUniformsiv(programId, 1, (GLuint*)&i, GL_UNIFORM_MATRIX_STRIDE, &uniform.m_matrixStride); + uniform.m_rawByteSize = uniformByteSize(uniform); + uniforms.append(uniform); + qCDebug(Render::Rendering) << uniform.m_name << "size" << uniform.m_size + << " offset" << uniform.m_offset + << " rawSize" << uniform.m_rawByteSize; + } + + return uniforms; +} + +QVector<ShaderAttribute> GraphicsHelperGL4::programAttributesAndLocations(GLuint programId) +{ + QVector<ShaderAttribute> attributes; + GLint nbrActiveAttributes = 0; + m_funcs->glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &nbrActiveAttributes); + attributes.reserve(nbrActiveAttributes); + char attributeName[256]; + for (GLint i = 0; i < nbrActiveAttributes; ++i) { + ShaderAttribute attribute; + GLsizei attributeNameLength = 0; + // Size is 1 for scalar and more for struct or arrays + // Type is the GL Type + m_funcs->glGetActiveAttrib(programId, i, sizeof(attributeName) - 1, &attributeNameLength, + &attribute.m_size, &attribute.m_type, attributeName); + attributeName[sizeof(attributeName) - 1] = '\0'; + attribute.m_location = m_funcs->glGetAttribLocation(programId, attributeName); + attribute.m_name = QString::fromUtf8(attributeName, attributeNameLength); + attributes.append(attribute); + } + return attributes; +} + +QVector<ShaderUniformBlock> GraphicsHelperGL4::programUniformBlocks(GLuint programId) +{ + QVector<ShaderUniformBlock> blocks; + GLint nbrActiveUniformsBlocks = 0; + m_funcs->glGetProgramInterfaceiv(programId, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &nbrActiveUniformsBlocks); + blocks.reserve(nbrActiveUniformsBlocks); + for (GLint i = 0; i < nbrActiveUniformsBlocks; ++i) { + QByteArray uniformBlockName(256, '\0'); + GLsizei length = 0; + ShaderUniformBlock uniformBlock; + m_funcs->glGetProgramResourceName(programId, GL_UNIFORM_BLOCK, i, 256, &length, uniformBlockName.data()); + uniformBlock.m_name = QString::fromUtf8(uniformBlockName.left(length)); + uniformBlock.m_index = i; + GLenum prop = GL_BUFFER_BINDING; + m_funcs->glGetProgramResourceiv(programId, GL_UNIFORM_BLOCK, i, 1, &prop, 4, NULL, &uniformBlock.m_binding); + prop = GL_BUFFER_DATA_SIZE; + m_funcs->glGetProgramResourceiv(programId, GL_UNIFORM_BLOCK, i, 1, &prop, 4, NULL, &uniformBlock.m_size); + prop = GL_NUM_ACTIVE_VARIABLES; + m_funcs->glGetProgramResourceiv(programId, GL_UNIFORM_BLOCK, i, 1, &prop, 4, NULL, &uniformBlock.m_activeUniformsCount); + blocks.append(uniformBlock); + } + return blocks; +} + +QVector<ShaderStorageBlock> GraphicsHelperGL4::programShaderStorageBlocks(GLuint programId) +{ + QVector<ShaderStorageBlock> blocks; + GLint nbrActiveShaderStorageBlocks = 0; + m_funcs->glGetProgramInterfaceiv(programId, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &nbrActiveShaderStorageBlocks); + blocks.reserve(nbrActiveShaderStorageBlocks); + for (GLint i = 0; i < nbrActiveShaderStorageBlocks; ++i) { + QByteArray storageBlockName(256, '\0'); + GLsizei length = 0; + ShaderStorageBlock storageBlock; + m_funcs->glGetProgramResourceName(programId, GL_SHADER_STORAGE_BLOCK, i, 256, &length, storageBlockName.data()); + storageBlock.m_index = i; + storageBlock.m_name = QString::fromUtf8(storageBlockName.left(length)); + GLenum prop = GL_BUFFER_BINDING; + m_funcs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_binding); + prop = GL_BUFFER_DATA_SIZE; + m_funcs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_size); + prop = GL_NUM_ACTIVE_VARIABLES; + m_funcs->glGetProgramResourceiv(programId, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 4, NULL, &storageBlock.m_activeVariablesCount); + blocks.push_back(storageBlock); + } + return blocks; +} + +void GraphicsHelperGL4::vertexAttribDivisor(GLuint index, GLuint divisor) +{ + m_funcs->glVertexAttribDivisor(index, divisor); +} + +void GraphicsHelperGL4::vertexAttributePointer(GLenum shaderDataType, + GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer) +{ + switch (shaderDataType) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + case GL_FLOAT_MAT4: + m_funcs->glVertexAttribPointer(index, size, type, normalized, stride, pointer); + break; + + case GL_INT: + case GL_INT_VEC2: + case GL_INT_VEC3: + case GL_INT_VEC4: + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_VEC2: + case GL_UNSIGNED_INT_VEC3: + case GL_UNSIGNED_INT_VEC4: + m_funcs->glVertexAttribIPointer(index, size, type, stride, pointer); + break; + + case GL_DOUBLE: + case GL_DOUBLE_VEC2: + case GL_DOUBLE_VEC3: + case GL_DOUBLE_VEC4: + m_funcs->glVertexAttribLPointer(index, size, type, stride, pointer); + break; + + default: + qCWarning(Render::Rendering) << "vertexAttribPointer: Unhandled type"; + Q_UNREACHABLE(); + } +} + +void GraphicsHelperGL4::readBuffer(GLenum mode) +{ + m_funcs->glReadBuffer(mode); +} + +void GraphicsHelperGL4::drawBuffer(GLenum mode) +{ + m_funcs->glDrawBuffer(mode); +} + +void GraphicsHelperGL4::glUniform1fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform1fv(location, count, values); +} + +void GraphicsHelperGL4::glUniform2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform2fv(location, count, values); +} + +void GraphicsHelperGL4::glUniform3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform3fv(location, count, values); +} + +void GraphicsHelperGL4::glUniform4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniform4fv(location, count, values); +} + +void GraphicsHelperGL4::glUniform1iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform1iv(location, count, values); +} + +void GraphicsHelperGL4::glUniform2iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform2iv(location, count, values); +} + +void GraphicsHelperGL4::glUniform3iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform3iv(location, count, values); +} + +void GraphicsHelperGL4::glUniform4iv(GLint location, GLsizei count, const GLint *values) +{ + m_funcs->glUniform4iv(location, count, values); +} + +void GraphicsHelperGL4::glUniform1uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform1uiv(location, count, values); +} + +void GraphicsHelperGL4::glUniform2uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform2uiv(location, count, values); +} + +void GraphicsHelperGL4::glUniform3uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform3uiv(location, count, values); +} + +void GraphicsHelperGL4::glUniform4uiv(GLint location, GLsizei count, const GLuint *values) +{ + m_funcs->glUniform4uiv(location, count, values); +} + +void GraphicsHelperGL4::glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2x3fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3x2fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix2x4fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4x2fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix3x4fv(location, count, false, values); +} + +void GraphicsHelperGL4::glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *values) +{ + m_funcs->glUniformMatrix4x3fv(location, count, false, values); +} + +UniformType GraphicsHelperGL4::uniformTypeFromGLType(GLenum type) +{ + switch (type) { + case GL_FLOAT: + return UniformType::Float; + case GL_FLOAT_VEC2: + return UniformType::Vec2; + case GL_FLOAT_VEC3: + return UniformType::Vec3; + case GL_FLOAT_VEC4: + return UniformType::Vec4; + case GL_FLOAT_MAT2: + return UniformType::Mat2; + case GL_FLOAT_MAT3: + return UniformType::Mat3; + case GL_FLOAT_MAT4: + return UniformType::Mat4; + case GL_FLOAT_MAT2x3: + return UniformType::Mat2x3; + case GL_FLOAT_MAT3x2: + return UniformType::Mat3x2; + case GL_FLOAT_MAT2x4: + return UniformType::Mat2x4; + case GL_FLOAT_MAT4x2: + return UniformType::Mat4x2; + case GL_FLOAT_MAT3x4: + return UniformType::Mat3x4; + case GL_FLOAT_MAT4x3: + return UniformType::Mat4x3; + case GL_INT: + return UniformType::Int; + case GL_INT_VEC2: + return UniformType::IVec2; + case GL_INT_VEC3: + return UniformType::IVec3; + case GL_INT_VEC4: + return UniformType::IVec4; + case GL_UNSIGNED_INT: + return UniformType::UInt; + case GL_UNSIGNED_INT_VEC2: + return UniformType::UIVec2; + case GL_UNSIGNED_INT_VEC3: + return UniformType::UIVec3; + case GL_UNSIGNED_INT_VEC4: + return UniformType::UIVec4; + case GL_BOOL: + return UniformType::Bool; + case GL_BOOL_VEC2: + return UniformType::BVec2; + case GL_BOOL_VEC3: + return UniformType::BVec3; + case GL_BOOL_VEC4: + return UniformType::BVec4; + + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_RECT: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_CUBE_MAP_ARRAY: + case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_3D: + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_CUBE_MAP_ARRAY: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: + return UniformType::Sampler; + default: + // TO DO: Add support for Doubles and Images + Q_UNREACHABLE(); + return UniformType::Float; + } +} + +void GraphicsHelperGL4::blendEquation(GLenum mode) +{ + m_funcs->glBlendEquation(mode); +} + +void GraphicsHelperGL4::blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) +{ + m_funcs->glBlendFunci(buf, sfactor, dfactor); +} + +void GraphicsHelperGL4::blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) +{ + m_funcs->glBlendFuncSeparatei(buf, sRGB, dRGB, sAlpha, dAlpha); +} + +void GraphicsHelperGL4::alphaTest(GLenum, GLenum) +{ + qCWarning(Render::Rendering) << "AlphaTest not available with OpenGL 3.2 core"; +} + +void GraphicsHelperGL4::depthTest(GLenum mode) +{ + m_funcs->glEnable(GL_DEPTH_TEST); + m_funcs->glDepthFunc(mode); +} + +void GraphicsHelperGL4::depthMask(GLenum mode) +{ + m_funcs->glDepthMask(mode); +} + +void GraphicsHelperGL4::frontFace(GLenum mode) +{ + m_funcs->glFrontFace(mode); + +} + +void GraphicsHelperGL4::setMSAAEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_MULTISAMPLE) + : m_funcs->glDisable(GL_MULTISAMPLE); +} + +void GraphicsHelperGL4::setAlphaCoverageEnabled(bool enabled) +{ + enabled ? m_funcs->glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE) + : m_funcs->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); +} + +GLuint GraphicsHelperGL4::createFrameBufferObject() +{ + GLuint id; + m_funcs->glGenFramebuffers(1, &id); + return id; +} + +void GraphicsHelperGL4::releaseFrameBufferObject(GLuint frameBufferId) +{ + m_funcs->glDeleteFramebuffers(1, &frameBufferId); +} + +void GraphicsHelperGL4::bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) +{ + switch (mode) { + case FBODraw: + m_funcs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferId); + return; + case FBORead: + m_funcs->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferId); + return; + case FBOReadAndDraw: + default: + m_funcs->glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId); + return; + } +} + +GLuint GraphicsHelperGL4::boundFrameBufferObject() +{ + GLint id = 0; + m_funcs->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &id); + return id; +} + +bool GraphicsHelperGL4::checkFrameBufferComplete() +{ + return (m_funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +} + +bool GraphicsHelperGL4::frameBufferNeedsRenderBuffer(const Attachment &attachment) +{ + Q_UNUSED(attachment); + return false; +} + +void GraphicsHelperGL4::bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) +{ + GLenum attr = GL_DEPTH_STENCIL_ATTACHMENT; + + if (attachment.m_point <= QRenderTargetOutput::Color15) + attr = GL_COLOR_ATTACHMENT0 + attachment.m_point; + else if (attachment.m_point == QRenderTargetOutput::Depth) + attr = GL_DEPTH_ATTACHMENT; + else if (attachment.m_point == QRenderTargetOutput::Stencil) + attr = GL_STENCIL_ATTACHMENT; + + texture->bind(); + QOpenGLTexture::Target target = texture->target(); + if (target == QOpenGLTexture::Target1DArray || target == QOpenGLTexture::Target2DArray || + target == QOpenGLTexture::Target2DMultisampleArray || target == QOpenGLTexture::Target3D) + m_funcs->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel, attachment.m_layer); + else if (target == QOpenGLTexture::TargetCubeMapArray) + m_funcs->glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel, attachment.m_layer); + else if (target == QOpenGLTexture::TargetCubeMap && attachment.m_face != QAbstractTexture::AllFaces) + m_funcs->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attr, attachment.m_face, texture->textureId(), attachment.m_mipLevel); + else + m_funcs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, attr, texture->textureId(), attachment.m_mipLevel); + texture->release(); +} + +void GraphicsHelperGL4::bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) +{ + Q_UNUSED(renderBuffer); + Q_UNUSED(attachment); + Q_UNREACHABLE(); +} + +bool GraphicsHelperGL4::supportsFeature(GraphicsHelperInterface::Feature feature) const +{ + switch (feature) { + case MRT: + case Tessellation: + case UniformBufferObject: + case BindableFragmentOutputs: + case PrimitiveRestart: + case RenderBufferDimensionRetrieval: + case TextureDimensionRetrieval: + case ShaderStorageObject: + case Compute: + case DrawBuffersBlend: + case BlitFramebuffer: + case IndirectDrawing: + return true; + default: + return false; + } +} + +void GraphicsHelperGL4::drawBuffers(GLsizei n, const int *bufs) +{ + // Use QVarLengthArray here + QVarLengthArray<GLenum, 16> drawBufs(n); + + for (int i = 0; i < n; i++) + drawBufs[i] = GL_COLOR_ATTACHMENT0 + bufs[i]; + m_funcs->glDrawBuffers(n, drawBufs.constData()); +} + +void GraphicsHelperGL4::bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) +{ + for (auto it = outputs.begin(), end = outputs.end(); it != end; ++it) + m_funcs->glBindFragDataLocation(shader, it.value(), it.key().toStdString().c_str()); +} + +void GraphicsHelperGL4::bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +{ + m_funcs->glUniformBlockBinding(programId, uniformBlockIndex, uniformBlockBinding); +} + +void GraphicsHelperGL4::bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) +{ + m_funcs->glShaderStorageBlockBinding(programId, shaderStorageBlockIndex, shaderStorageBlockBinding); +} + +void GraphicsHelperGL4::bindBufferBase(GLenum target, GLuint index, GLuint buffer) +{ + m_funcs->glBindBufferBase(target, index, buffer); +} + +void GraphicsHelperGL4::buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) +{ + char *bufferData = buffer.data(); + + switch (description.m_type) { + + case GL_FLOAT: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_FLOAT_VEC2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_FLOAT_VEC3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_FLOAT_VEC4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_FLOAT_MAT2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 4); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 2); + break; + } + + case GL_FLOAT_MAT2x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 3); + break; + } + + case GL_FLOAT_MAT2x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 2, 4); + break; + } + + case GL_FLOAT_MAT3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 9); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 3); + break; + } + + case GL_FLOAT_MAT3x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 6); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 2); + break; + } + + case GL_FLOAT_MAT3x4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 3, 4); + break; + } + + case GL_FLOAT_MAT4: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 16); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 4); + break; + } + + case GL_FLOAT_MAT4x2: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 8); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 2); + break; + } + + case GL_FLOAT_MAT4x3: { + const GLfloat *data = QGraphicsUtils::valueArrayFromVariant<GLfloat>(v, description.m_size, 12); + QGraphicsUtils::fillDataMatrixArray(bufferData, data, description, 4, 3); + break; + } + + case GL_INT: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_INT_VEC2: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_INT_VEC3: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_INT_VEC4: { + const GLint *data = QGraphicsUtils::valueArrayFromVariant<GLint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_UNSIGNED_INT: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_UNSIGNED_INT_VEC2: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_UNSIGNED_INT_VEC3: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_UNSIGNED_INT_VEC4: { + const GLuint *data = QGraphicsUtils::valueArrayFromVariant<GLuint>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_BOOL: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 1); + QGraphicsUtils::fillDataArray(bufferData, data, description, 1); + break; + } + + case GL_BOOL_VEC2: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 2); + QGraphicsUtils::fillDataArray(bufferData, data, description, 2); + break; + } + + case GL_BOOL_VEC3: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 3); + QGraphicsUtils::fillDataArray(bufferData, data, description, 3); + break; + } + + case GL_BOOL_VEC4: { + const GLboolean *data = QGraphicsUtils::valueArrayFromVariant<GLboolean>(v, description.m_size, 4); + QGraphicsUtils::fillDataArray(bufferData, data, description, 4); + break; + } + + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: { + Q_ASSERT(description.m_size == 1); + int value = v.toInt(); + QGraphicsUtils::fillDataArray<GLint>(bufferData, &value, description, 1); + break; + } + + default: + qWarning() << Q_FUNC_INFO << "unsupported uniform type:" << description.m_type << "for " << description.m_name; + break; + } +} + +uint GraphicsHelperGL4::uniformByteSize(const ShaderUniform &description) +{ + uint rawByteSize = 0; + int arrayStride = qMax(description.m_arrayStride, 0); + int matrixStride = qMax(description.m_matrixStride, 0); + + switch (description.m_type) { + + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_UNSIGNED_INT_VEC2: + rawByteSize = 8; + break; + + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + case GL_UNSIGNED_INT_VEC3: + rawByteSize = 12; + break; + + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + case GL_UNSIGNED_INT_VEC4: + rawByteSize = 16; + break; + + case GL_FLOAT_MAT2: + rawByteSize = matrixStride ? 2 * matrixStride : 16; + break; + + case GL_FLOAT_MAT2x4: + rawByteSize = matrixStride ? 2 * matrixStride : 32; + break; + + case GL_FLOAT_MAT4x2: + rawByteSize = matrixStride ? 4 * matrixStride : 32; + break; + + case GL_FLOAT_MAT3: + rawByteSize = matrixStride ? 3 * matrixStride : 36; + break; + + case GL_FLOAT_MAT2x3: + rawByteSize = matrixStride ? 2 * matrixStride : 24; + break; + + case GL_FLOAT_MAT3x2: + rawByteSize = matrixStride ? 3 * matrixStride : 24; + break; + + case GL_FLOAT_MAT4: + rawByteSize = matrixStride ? 4 * matrixStride : 64; + break; + + case GL_FLOAT_MAT4x3: + rawByteSize = matrixStride ? 4 * matrixStride : 48; + break; + + case GL_FLOAT_MAT3x4: + rawByteSize = matrixStride ? 3 * matrixStride : 48; + break; + + case GL_BOOL: + rawByteSize = 1; + break; + + case GL_BOOL_VEC2: + rawByteSize = 2; + break; + + case GL_BOOL_VEC3: + rawByteSize = 3; + break; + + case GL_BOOL_VEC4: + rawByteSize = 4; + break; + + case GL_INT: + case GL_FLOAT: + case GL_UNSIGNED_INT: + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + rawByteSize = 4; + break; + } + + return arrayStride ? rawByteSize * arrayStride : rawByteSize; +} + +void GraphicsHelperGL4::enableClipPlane(int clipPlane) +{ + m_funcs->glEnable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL4::disableClipPlane(int clipPlane) +{ + m_funcs->glDisable(GL_CLIP_DISTANCE0 + clipPlane); +} + +void GraphicsHelperGL4::setClipPlane(int, const QVector3D &, float) +{ + // deprecated +} + +GLint GraphicsHelperGL4::maxClipPlaneCount() +{ + GLint max = 0; + m_funcs->glGetIntegerv(GL_MAX_CLIP_DISTANCES, &max); + return max; +} + +void GraphicsHelperGL4::memoryBarrier(QMemoryBarrier::Operations barriers) +{ + m_funcs->glMemoryBarrier(memoryBarrierGLBitfield(barriers)); +} + +void GraphicsHelperGL4::enablePrimitiveRestart(int primitiveRestartIndex) +{ + m_funcs->glPrimitiveRestartIndex(primitiveRestartIndex); + m_funcs->glEnable(GL_PRIMITIVE_RESTART); +} + +void GraphicsHelperGL4::enableVertexAttributeArray(int location) +{ + m_funcs->glEnableVertexAttribArray(location); +} + +void GraphicsHelperGL4::disablePrimitiveRestart() +{ + m_funcs->glDisable(GL_PRIMITIVE_RESTART); +} + +void GraphicsHelperGL4::clearBufferf(GLint drawbuffer, const QVector4D &values) +{ + GLfloat vec[4] = {values[0], values[1], values[2], values[3]}; + m_funcs->glClearBufferfv(GL_COLOR, drawbuffer, vec); +} + +void GraphicsHelperGL4::pointSize(bool programmable, GLfloat value) +{ + if (programmable) { + m_funcs->glEnable(GL_PROGRAM_POINT_SIZE); + } else { + m_funcs->glDisable(GL_PROGRAM_POINT_SIZE); + m_funcs->glPointSize(value); + } +} + +void GraphicsHelperGL4::enablei(GLenum cap, GLuint index) +{ + m_funcs->glEnablei(cap, index); +} + +void GraphicsHelperGL4::disablei(GLenum cap, GLuint index) +{ + m_funcs->glDisablei(cap, index); +} + +void GraphicsHelperGL4::setSeamlessCubemap(bool enable) +{ + if (enable) + m_funcs->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + else + m_funcs->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS); +} + +QSize GraphicsHelperGL4::getRenderBufferDimensions(GLuint renderBufferId) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); + m_funcs->glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); + m_funcs->glBindRenderbuffer(GL_RENDERBUFFER, 0); + + return QSize(width, height); +} + +QSize GraphicsHelperGL4::getTextureDimensions(GLuint textureId, GLenum target, uint level) +{ + GLint width = 0; + GLint height = 0; + + m_funcs->glBindTexture(target, textureId); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width); + m_funcs->glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height); + m_funcs->glBindTexture(target, 0); + + return QSize(width, height); +} + +void GraphicsHelperGL4::dispatchCompute(GLuint wx, GLuint wy, GLuint wz) +{ + m_funcs->glDispatchCompute(wx, wy, wz); +} + +char *GraphicsHelperGL4::mapBuffer(GLenum target, GLsizeiptr size) +{ + return static_cast<char*>(m_funcs->glMapBufferRange(target, 0, size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); +} + +GLboolean GraphicsHelperGL4::unmapBuffer(GLenum target) +{ + return m_funcs->glUnmapBuffer(target); +} + +void GraphicsHelperGL4::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) +{ + m_funcs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h new file mode 100644 index 000000000..3020b16d8 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERGL4_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERGL4_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <QtCore/qscopedpointer.h> + +#ifndef QT_OPENGL_ES_2 + +QT_BEGIN_NAMESPACE + +class QOpenGLFunctions_4_3_Core; + +namespace Qt3DRender { +namespace Render { + +class Q_AUTOTEST_EXPORT GraphicsHelperGL4 : public GraphicsHelperInterface +{ +public: + GraphicsHelperGL4(); + + // QGraphicHelperInterface interface + void alphaTest(GLenum mode1, GLenum mode2) override; + void bindBufferBase(GLenum target, GLuint index, GLuint buffer) override; + void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) override; + bool frameBufferNeedsRenderBuffer(const Attachment &attachment) override; + void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) override; + void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) override; + void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) override; + void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) override; + void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) override; + void blendEquation(GLenum mode) override; + void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) override; + void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) override; + void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) override; + GLuint boundFrameBufferObject() override; + void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) override; + bool checkFrameBufferComplete() override; + void clearBufferf(GLint drawbuffer, const QVector4D &values) override; + GLuint createFrameBufferObject() override; + void depthMask(GLenum mode) override; + void depthTest(GLenum mode) override; + void disableClipPlane(int clipPlane) override; + void disablei(GLenum cap, GLuint index) override; + void disablePrimitiveRestart() override; + void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) override; + char *mapBuffer(GLenum target, GLsizeiptr size) override; + GLboolean unmapBuffer(GLenum target) override; + void drawArrays(GLenum primitiveType, GLint first, GLsizei count) override; + void drawArraysIndirect(GLenum mode,void *indirect) override; + void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) override; + void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseInstance) override; + void drawBuffers(GLsizei n, const int *bufs) override; + void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLint baseVertex = 0) override; + void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) override; + void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void *indices, GLsizei instances, GLint baseVertex = 0, GLint baseInstance = 0) override; + void enableClipPlane(int clipPlane) override; + void enablei(GLenum cap, GLuint index) override; + void enablePrimitiveRestart(int primitiveRestartIndex) override; + void enableVertexAttributeArray(int location) override; + void frontFace(GLenum mode) override; + QSize getRenderBufferDimensions(GLuint renderBufferId) override; + QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) override; + void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) override; + void pointSize(bool programmable, GLfloat value) override; + GLint maxClipPlaneCount() override; + void memoryBarrier(QMemoryBarrier::Operations barriers) override; + QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) override; + QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) override; + QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) override; + QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) override; + void releaseFrameBufferObject(GLuint frameBufferId) override; + void setMSAAEnabled(bool enable) override; + void setAlphaCoverageEnabled(bool enable) override; + void setClipPlane(int clipPlane, const QVector3D &normal, float distance) override; + void setSeamlessCubemap(bool enable) override; + void setVerticesPerPatch(GLint verticesPerPatch) override; + bool supportsFeature(Feature feature) const override; + uint uniformByteSize(const ShaderUniform &description) override; + void useProgram(GLuint programId) override; + void vertexAttribDivisor(GLuint index, GLuint divisor) override; + void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) override; + void readBuffer(GLenum mode) override; + void drawBuffer(GLenum mode) override; + + void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) override; + + void glUniform1iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform2iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform3iv(GLint location, GLsizei count, const GLint *value) override; + void glUniform4iv(GLint location, GLsizei count, const GLint *value) override; + + void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) override; + void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) override; + + void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) override; + void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) override; + + UniformType uniformTypeFromGLType(GLenum glType) override; + +private: + QOpenGLFunctions_4_3_Core *m_funcs; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // !QT_OPENGL_ES_2 + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERGL4_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h new file mode 100644 index 000000000..e41325cb7 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GRAPHICSHELPERINTERFACE_H +#define QT3DRENDER_RENDER_GRAPHICSHELPERINTERFACE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QOpenGLFunctions> +#include <QOpenGLTexture> +#include <QVector> +#include <Qt3DRender/private/shadervariables_p.h> +#include <Qt3DRender/private/uniform_p.h> +#include <Qt3DRender/qmemorybarrier.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +struct Attachment; +class RenderBuffer; + +class GraphicsHelperInterface +{ +public: + enum Feature { + MRT = 0, + Tessellation, + UniformBufferObject, + BindableFragmentOutputs, + PrimitiveRestart, + RenderBufferDimensionRetrieval, + TextureDimensionRetrieval, + ShaderStorageObject, + Compute, + DrawBuffersBlend, + BlitFramebuffer, + IndirectDrawing, + MapBuffer + }; + + enum FBOBindMode { + FBODraw, + FBORead, + FBOReadAndDraw + }; + + virtual ~GraphicsHelperInterface() {} + virtual void alphaTest(GLenum mode1, GLenum mode2) = 0; + virtual void bindBufferBase(GLenum target, GLuint index, GLuint buffer) = 0; + virtual void bindFragDataLocation(GLuint shader, const QHash<QString, int> &outputs) = 0; + virtual bool frameBufferNeedsRenderBuffer(const Attachment &attachment) = 0; + virtual void bindFrameBufferAttachment(QOpenGLTexture *texture, const Attachment &attachment) = 0; + virtual void bindFrameBufferAttachment(RenderBuffer *renderBuffer, const Attachment &attachment) = 0; + virtual void bindFrameBufferObject(GLuint frameBufferId, FBOBindMode mode) = 0; + virtual void bindShaderStorageBlock(GLuint programId, GLuint shaderStorageBlockIndex, GLuint shaderStorageBlockBinding) = 0; + virtual void bindUniformBlock(GLuint programId, GLuint uniformBlockIndex, GLuint uniformBlockBinding) = 0; + virtual void blendEquation(GLenum mode) = 0; + virtual void blendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) = 0; + virtual void blendFuncSeparatei(GLuint buf, GLenum sRGB, GLenum dRGB, GLenum sAlpha, GLenum dAlpha) = 0; + virtual void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) = 0; + virtual GLuint boundFrameBufferObject() = 0; + virtual void buildUniformBuffer(const QVariant &v, const ShaderUniform &description, QByteArray &buffer) = 0; + virtual bool checkFrameBufferComplete() = 0; + virtual void clearBufferf(GLint drawbuffer, const QVector4D &values) = 0; + virtual GLuint createFrameBufferObject() = 0; + virtual void depthMask(GLenum mode) = 0; + virtual void depthTest(GLenum mode) = 0; + virtual void disableClipPlane(int clipPlane) = 0; + virtual void disablei(GLenum cap, GLuint index) = 0; + virtual void disablePrimitiveRestart() = 0; + virtual void dispatchCompute(GLuint wx, GLuint wy, GLuint wz) = 0; + virtual char *mapBuffer(GLenum target, GLsizeiptr size) = 0; + virtual GLboolean unmapBuffer(GLenum target) = 0; + virtual void drawArrays(GLenum primitiveType, GLint first, GLsizei count) = 0; + virtual void drawArraysIndirect(GLenum mode,void *indirect) = 0; + virtual void drawArraysInstanced(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances) = 0; + virtual void drawArraysInstancedBaseInstance(GLenum primitiveType, GLint first, GLsizei count, GLsizei instances, GLsizei baseinstance) = 0; + virtual void drawBuffers(GLsizei n, const int *bufs) = 0; + virtual void drawElements(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLint baseVertex) = 0; + virtual void drawElementsIndirect(GLenum mode, GLenum type, void *indirect) = 0; + virtual void drawElementsInstancedBaseVertexBaseInstance(GLenum primitiveType, GLsizei primitiveCount, GLint indexType, void * indices, GLsizei instances, GLint baseVertex, GLint baseInstance) = 0; + virtual void enableClipPlane(int clipPlane) = 0; + virtual void enablei(GLenum cap, GLuint index) = 0; + virtual void enablePrimitiveRestart(int primitiveRestartIndex) = 0; + virtual void enableVertexAttributeArray(int location) = 0; + virtual void frontFace(GLenum mode) = 0; + virtual QSize getRenderBufferDimensions(GLuint renderBufferId) = 0; + virtual QSize getTextureDimensions(GLuint textureId, GLenum target, uint level = 0) = 0; + virtual void initializeHelper(QOpenGLContext *context, QAbstractOpenGLFunctions *functions) = 0; + virtual GLint maxClipPlaneCount() = 0; + virtual void memoryBarrier(QMemoryBarrier::Operations barriers) = 0; + virtual void pointSize(bool programmable, GLfloat value) = 0; + virtual QVector<ShaderAttribute> programAttributesAndLocations(GLuint programId) = 0; + virtual QVector<ShaderUniform> programUniformsAndLocations(GLuint programId) = 0; + virtual QVector<ShaderUniformBlock> programUniformBlocks(GLuint programId) = 0; + virtual QVector<ShaderStorageBlock> programShaderStorageBlocks(GLuint programId) = 0; + virtual void releaseFrameBufferObject(GLuint frameBufferId) = 0; + virtual void setAlphaCoverageEnabled(bool enable) = 0; + virtual void setClipPlane(int clipPlane, const QVector3D &normal, float distance) = 0; + virtual void setMSAAEnabled(bool enable) = 0; + virtual void setSeamlessCubemap(bool enable) = 0; + virtual void setVerticesPerPatch(GLint verticesPerPatch) = 0; + virtual bool supportsFeature(Feature feature) const = 0; + virtual uint uniformByteSize(const ShaderUniform &description) = 0; + virtual void useProgram(GLuint programId) = 0; + virtual void vertexAttribDivisor(GLuint index, GLuint divisor) = 0; + virtual void vertexAttributePointer(GLenum shaderDataType, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) = 0; + virtual void readBuffer(GLenum mode) = 0; + virtual void drawBuffer(GLenum mode) = 0; + + virtual void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) = 0; + + virtual void glUniform1iv(GLint location, GLsizei count, const GLint *value) = 0; + virtual void glUniform2iv(GLint location, GLsizei count, const GLint *value) = 0; + virtual void glUniform3iv(GLint location, GLsizei count, const GLint *value) = 0; + virtual void glUniform4iv(GLint location, GLsizei count, const GLint *value) = 0; + + virtual void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) = 0; + virtual void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) = 0; + virtual void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) = 0; + virtual void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) = 0; + + virtual void glUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix2x3fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix3x2fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix2x4fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix4x2fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix3x4fv(GLint location, GLsizei count, const GLfloat *value) = 0; + virtual void glUniformMatrix4x3fv(GLint location, GLsizei count, const GLfloat *value) = 0; + + virtual UniformType uniformTypeFromGLType(GLenum glType) = 0; +}; + + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GRAPHICSHELPERINTERFACE_H diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri new file mode 100644 index 000000000..b3698858a --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri @@ -0,0 +1,26 @@ +#DEFINES += QT3D_RENDER_ASPECT_OPENGL_DEBUG + +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/graphicscontext_p.h \ + $$PWD/graphicshelperinterface_p.h \ + $$PWD/graphicshelperes2_p.h \ + $$PWD/graphicshelperes3_p.h \ + $$PWD/graphicshelperes3_2_p.h \ + $$PWD/graphicshelpergl2_p.h \ + $$PWD/graphicshelpergl3_3_p.h \ + $$PWD/graphicshelpergl4_p.h \ + $$PWD/graphicshelpergl3_2_p.h \ + $$PWD/submissioncontext_p.h + +SOURCES += \ + $$PWD/graphicscontext.cpp \ + $$PWD/graphicshelperes2.cpp \ + $$PWD/graphicshelperes3.cpp \ + $$PWD/graphicshelperes3_2.cpp \ + $$PWD/graphicshelpergl2.cpp \ + $$PWD/graphicshelpergl3_3.cpp \ + $$PWD/graphicshelpergl4.cpp \ + $$PWD/graphicshelpergl3_2.cpp \ + $$PWD/submissioncontext.cpp diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp new file mode 100644 index 000000000..df7d61902 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp @@ -0,0 +1,1211 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "submissioncontext_p.h" + +#include <Qt3DRender/qgraphicsapifilter.h> +#include <Qt3DRender/qparameter.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/material_p.h> +#include <Qt3DRender/private/gltexture_p.h> +#include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/attribute_p.h> +#include <Qt3DRender/private/rendercommand_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/rendertarget_p.h> +#include <Qt3DRender/private/graphicshelperinterface_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/gltexturemanager_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/qbuffer_p.h> +#include <Qt3DRender/private/renderbuffer_p.h> +#include <QOpenGLShaderProgram> + +#if !defined(QT_OPENGL_ES_2) +#include <QOpenGLFunctions_2_0> +#include <QOpenGLFunctions_3_2_Core> +#include <QOpenGLFunctions_3_3_Core> +#include <QOpenGLFunctions_4_3_Core> +#include <Qt3DRender/private/graphicshelpergl2_p.h> +#include <Qt3DRender/private/graphicshelpergl3_2_p.h> +#include <Qt3DRender/private/graphicshelpergl3_3_p.h> +#include <Qt3DRender/private/graphicshelpergl4_p.h> +#endif +#include <Qt3DRender/private/graphicshelperes2_p.h> +#include <Qt3DRender/private/graphicshelperes3_p.h> + +#include <QSurface> +#include <QWindow> +#include <QOpenGLTexture> +#include <QOpenGLDebugLogger> + +QT_BEGIN_NAMESPACE + +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif + +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +namespace Qt3DRender { +namespace Render { + +static QHash<unsigned int, SubmissionContext*> static_contexts; + +namespace { + +GLBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type) +{ + switch (type) { + case QAttribute::VertexAttribute: + return GLBuffer::ArrayBuffer; + case QAttribute::IndexAttribute: + return GLBuffer::IndexBuffer; + case QAttribute::DrawIndirectAttribute: + return GLBuffer::DrawIndirectBuffer; + default: + Q_UNREACHABLE(); + } +} + +void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width, uint height, QAbstractTexture::TextureFormat format) +{ + switch (format) { + case QAbstractTexture::RGBA32F: + { + uchar *srcScanline = (uchar *)srcData + stride * (height - 1); + for (uint i = 0; i < height; ++i) { + uchar *dstScanline = img.scanLine(i); + float *pSrc = (float*)srcScanline; + for (uint j = 0; j < width; j++) { + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+2], 1.0f)); + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+1], 1.0f)); + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+0], 1.0f)); + *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+3], 1.0f)); + } + srcScanline -= stride; + } + } break; + default: + { + uchar* srcScanline = (uchar *)srcData + stride * (height - 1); + for (uint i = 0; i < height; ++i) { + memcpy(img.scanLine(i), srcScanline, stride); + srcScanline -= stride; + } + } break; + } +} + +} // anonymous + +unsigned int nextFreeContextId() +{ + for (unsigned int i=0; i < 0xffff; ++i) { + if (!static_contexts.contains(i)) + return i; + } + + qFatal("Couldn't find free context ID"); + return 0; +} + +SubmissionContext::SubmissionContext() + : GraphicsContext() + , m_initialized(false) + , m_ownCurrent(true) + , m_id(nextFreeContextId()) + , m_surface(nullptr) + , m_activeShader(nullptr) + , m_activeShaderDNA(0) + , m_renderTargetFormat(QAbstractTexture::NoFormat) + , m_currClearStencilValue(0) + , m_currClearDepthValue(1.f) + , m_currClearColorValue(0,0,0,0) + , m_material(nullptr) + , m_activeFBO(0) + , m_boundArrayBuffer(nullptr) + , m_stateSet(nullptr) + , m_renderer(nullptr) + , m_uboTempArray(QByteArray(1024, 0)) + , m_currentVAO(nullptr) +{ + static_contexts[m_id] = this; +} + +SubmissionContext::~SubmissionContext() +{ + releaseOpenGL(); + + Q_ASSERT(static_contexts[m_id] == this); + static_contexts.remove(m_id); +} + +void SubmissionContext::initialize() +{ + GraphicsContext::initialize(); + m_activeTextures.resize(maxTextureUnitsCount()); +} + +void SubmissionContext::resolveRenderTargetFormat() +{ + const QSurfaceFormat format = m_gl->format(); + const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize(); + const uint r = format.redBufferSize(); + const uint g = format.greenBufferSize(); + const uint b = format.blueBufferSize(); + +#define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18)) + + const uint bits = RGBA_BITS(r,g,b,a); + switch (bits) { + case RGBA_BITS(8,8,8,8): + m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm; + break; + case RGBA_BITS(8,8,8,0): + m_renderTargetFormat = QAbstractTexture::RGB8_UNorm; + break; + case RGBA_BITS(5,6,5,0): + m_renderTargetFormat = QAbstractTexture::R5G6B5; + break; + } +#undef RGBA_BITS +} + +bool SubmissionContext::beginDrawing(QSurface *surface) +{ + Q_ASSERT(surface); + Q_ASSERT(m_gl); + + m_surface = surface; + + // TO DO: Find a way to make to pause work if the window is not exposed + // if (m_surface && m_surface->surfaceClass() == QSurface::Window) { + // qDebug() << Q_FUNC_INFO << 1; + // if (!static_cast<QWindow *>(m_surface)->isExposed()) + // return false; + // qDebug() << Q_FUNC_INFO << 2; + // } + + // Makes the surface current on the OpenGLContext + // and sets the right glHelper + m_ownCurrent = !(m_gl->surface() == m_surface); + if (m_ownCurrent && !makeCurrent(m_surface)) + return false; + + // TODO: cache surface format somewhere rather than doing this every time render surface changes + resolveRenderTargetFormat(); + + // Sets or Create the correct m_glHelper + // for the current surface + activateGLHelper(); + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + GLint err = m_gl->functions()->glGetError(); + if (err != 0) { + qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err; + } +#endif + + if (!m_initialized) { + initialize(); + } + + // need to reset these values every frame, may get overwritten elsewhere + m_gl->functions()->glClearColor(m_currClearColorValue.redF(), m_currClearColorValue.greenF(), m_currClearColorValue.blueF(), m_currClearColorValue.alphaF()); + m_gl->functions()->glClearDepthf(m_currClearDepthValue); + m_gl->functions()->glClearStencil(m_currClearStencilValue); + + + if (m_activeShader) { + m_activeShader = nullptr; + m_activeShaderDNA = 0; + } + + // reset active textures + for (int u = 0; u < m_activeTextures.size(); ++u) + m_activeTextures[u].texture = nullptr; + + m_boundArrayBuffer = nullptr; + + static int callCount = 0; + ++callCount; + const int shaderPurgePeriod = 600; + if (callCount % shaderPurgePeriod == 0) + m_shaderCache->purge(); + + return true; +} + +void SubmissionContext::endDrawing(bool swapBuffers) +{ + if (swapBuffers) + m_gl->swapBuffers(m_surface); + if (m_ownCurrent) + m_gl->doneCurrent(); + decayTextureScores(); +} + +void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId) +{ + GLuint fboId = defaultFboId; // Default FBO + if (renderTargetNodeId) { + // New RenderTarget + if (!m_renderTargets.contains(renderTargetNodeId)) { + if (m_defaultFBO && fboId == m_defaultFBO) { + // this is the default fbo that some platforms create (iOS), we just register it + // Insert FBO into hash + m_renderTargets.insert(renderTargetNodeId, fboId); + } else { + fboId = createRenderTarget(renderTargetNodeId, attachments); + } + } else { + fboId = updateRenderTarget(renderTargetNodeId, attachments, true); + } + } + m_activeFBO = fboId; + m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw); + // Set active drawBuffers + activateDrawBuffers(attachments); +} + +GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments) +{ + const GLuint fboId = m_glHelper->createFrameBufferObject(); + if (fboId) { + // The FBO is created and its attachments are set once + // Insert FBO into hash + m_renderTargets.insert(renderTargetNodeId, fboId); + // Bind FBO + m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); + bindFrameBufferAttachmentHelper(fboId, attachments); + } else { + qCritical("Failed to create FBO"); + } + return fboId; +} + +GLuint SubmissionContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget) +{ + const GLuint fboId = m_renderTargets.value(renderTargetNodeId); + + // We need to check if one of the attachment was resized + bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet? + if (!needsResize) { + // render target exists, has attachment been resized? + GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager(); + const QSize s = m_renderTargetsSize[fboId]; + const auto attachments_ = attachments.attachments(); + for (const Attachment &attachment : attachments_) { + GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid); + // ### TODO QTBUG-64757 this check is insufficient since the + // texture may have changed to another one with the same size. That + // case is not handled atm. + needsResize |= (rTex != nullptr && rTex->size() != s); + if (isActiveRenderTarget) { + if (attachment.m_point == QRenderTargetOutput::Color0) + m_renderTargetFormat = rTex->properties().format; + } + } + } + + if (needsResize) { + m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); + bindFrameBufferAttachmentHelper(fboId, attachments); + } + + return fboId; +} + +QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const +{ + QSize renderTargetSize; + if (m_activeFBO != m_defaultFBO) { + // For external FBOs we may not have a m_renderTargets entry. + if (m_renderTargetsSize.contains(m_activeFBO)) { + renderTargetSize = m_renderTargetsSize[m_activeFBO]; + } else if (surfaceSize.isValid()) { + renderTargetSize = surfaceSize; + } else { + // External FBO (when used with QtQuick2 Scene3D) + + // Query FBO color attachment 0 size + GLint attachmentObjectType = GL_NONE; + GLint attachment0Name = 0; + m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + &attachmentObjectType); + m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &attachment0Name); + + if (attachmentObjectType == GL_RENDERBUFFER && m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval)) + renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name); + else if (attachmentObjectType == GL_TEXTURE && m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval)) + // Assumes texture level 0 and GL_TEXTURE_2D target + renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name, GL_TEXTURE_2D); + else + return renderTargetSize; + } + } else { + renderTargetSize = m_surface->size(); + if (m_surface->surfaceClass() == QSurface::Window) { + int dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio(); + renderTargetSize *= dpr; + } + } + return renderTargetSize; +} + +QImage SubmissionContext::readFramebuffer(const QRect &rect) +{ + QImage img; + const unsigned int area = rect.width() * rect.height(); + unsigned int bytes; + GLenum format, type; + QImage::Format imageFormat; + uint stride; + + /* format value should match GL internalFormat */ + GLenum internalFormat = m_renderTargetFormat; + + switch (m_renderTargetFormat) { + case QAbstractTexture::RGBAFormat: + case QAbstractTexture::RGBA8_SNorm: + case QAbstractTexture::RGBA8_UNorm: + case QAbstractTexture::RGBA8U: + case QAbstractTexture::SRGB8_Alpha8: +#ifdef QT_OPENGL_ES_2 + format = GL_RGBA; + imageFormat = QImage::Format_RGBA8888_Premultiplied; +#else + format = GL_BGRA; + imageFormat = QImage::Format_ARGB32_Premultiplied; + internalFormat = GL_RGBA8; +#endif + type = GL_UNSIGNED_BYTE; + bytes = area * 4; + stride = rect.width() * 4; + break; + case QAbstractTexture::SRGB8: + case QAbstractTexture::RGBFormat: + case QAbstractTexture::RGB8U: + case QAbstractTexture::RGB8_UNorm: +#ifdef QT_OPENGL_ES_2 + format = GL_RGBA; + imageFormat = QImage::Format_RGBX8888; +#else + format = GL_BGRA; + imageFormat = QImage::Format_RGB32; + internalFormat = GL_RGB8; +#endif + type = GL_UNSIGNED_BYTE; + bytes = area * 4; + stride = rect.width() * 4; + break; +#ifndef QT_OPENGL_ES_2 + case QAbstractTexture::RG11B10F: + bytes = area * 4; + format = GL_RGB; + type = GL_UNSIGNED_INT_10F_11F_11F_REV; + imageFormat = QImage::Format_RGB30; + stride = rect.width() * 4; + break; + case QAbstractTexture::RGB10A2: + bytes = area * 4; + format = GL_RGBA; + type = GL_UNSIGNED_INT_2_10_10_10_REV; + imageFormat = QImage::Format_A2BGR30_Premultiplied; + stride = rect.width() * 4; + break; + case QAbstractTexture::R5G6B5: + bytes = area * 2; + format = GL_RGB; + type = GL_UNSIGNED_SHORT; + internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV; + imageFormat = QImage::Format_RGB16; + stride = rect.width() * 2; + break; + case QAbstractTexture::RGBA16F: + case QAbstractTexture::RGBA16U: + case QAbstractTexture::RGBA32F: + case QAbstractTexture::RGBA32U: + bytes = area * 16; + format = GL_RGBA; + type = GL_FLOAT; + imageFormat = QImage::Format_ARGB32_Premultiplied; + stride = rect.width() * 16; + break; +#endif + default: + // unsupported format + Q_UNREACHABLE(); + return img; + } + + GLint samples = 0; + m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples); + if (samples > 0 && !m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) { + qWarning () << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; " + "Required feature BlitFramebuffer is missing."; + return img; + } + + img = QImage(rect.width(), rect.height(), imageFormat); + + QScopedArrayPointer<uchar> data(new uchar [bytes]); + + if (samples > 0) { + // resolve multisample-framebuffer to renderbuffer and read pixels from it + GLuint fbo, rb; + QOpenGLFunctions *gl = m_gl->functions(); + gl->glGenFramebuffers(1, &fbo); + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + gl->glGenRenderbuffers(1, &rb); + gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); + gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(), rect.height()); + gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); + + const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + gl->glDeleteRenderbuffers(1, &rb); + gl->glDeleteFramebuffers(1, &fbo); + qWarning () << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status; + return img; + } + + m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(), + 0, 0, rect.width(), rect.height(), + GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data()); + + copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); + + gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); + gl->glDeleteRenderbuffers(1, &rb); + gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO); + gl->glDeleteFramebuffers(1, &fbo); + } else { + // read pixels directly from framebuffer + m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), format, type, data.data()); + copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); + } + + return img; +} + +void SubmissionContext::setViewport(const QRectF &viewport, const QSize &surfaceSize) +{ + // // save for later use; this has nothing to do with the viewport but it is + // // here that we get to know the surfaceSize from the RenderView. + m_surfaceSize = surfaceSize; + + m_viewport = viewport; + QSize size = renderTargetSize(surfaceSize); + + // Check that the returned size is before calling glViewport + if (size.isEmpty()) + return; + + // Qt3D 0------------------> 1 OpenGL 1^ + // | | + // | | + // | | + // V | + // 1 0---------------------> 1 + // The Viewport is defined between 0 and 1 which allows us to automatically + // scale to the size of the provided window surface + m_gl->functions()->glViewport(m_viewport.x() * size.width(), + (1.0 - m_viewport.y() - m_viewport.height()) * size.height(), + m_viewport.width() * size.width(), + m_viewport.height() * size.height()); +} + +void SubmissionContext::releaseOpenGL() +{ + m_shaderCache->clear(); + m_renderBufferHash.clear(); + + // Stop and destroy the OpenGL logger + if (m_debugLogger) { + m_debugLogger->stopLogging(); + m_debugLogger.reset(nullptr); + } +} + +// The OpenGLContext is not current on any surface at this point +void SubmissionContext::setOpenGLContext(QOpenGLContext* ctx) +{ + Q_ASSERT(ctx && m_shaderCache); + + releaseOpenGL(); + m_gl = ctx; +} + +void SubmissionContext::activateGLHelper() +{ + // Sets the correct GL Helper depending on the surface + // If no helper exists, create one + m_glHelper = m_glHelpers.value(m_surface); + if (!m_glHelper) { + m_glHelper = resolveHighestOpenGLFunctions(); + m_glHelpers.insert(m_surface, m_glHelper); + // Note: OpenGLContext is current at this point + m_gl->functions()->glDisable(GL_DITHER); + } +} + + +// Called only from RenderThread +bool SubmissionContext::activateShader(ProgramDNA shaderDNA) +{ + if (shaderDNA != m_activeShaderDNA) { + // Ensure material uniforms are re-applied + m_material = nullptr; + + m_activeShader = m_shaderCache->getShaderProgramForDNA(shaderDNA); + if (Q_LIKELY(m_activeShader != nullptr)) { + m_activeShader->bind(); + m_activeShaderDNA = shaderDNA; + } else { + m_glHelper->useProgram(0); + qWarning() << "No shader program found for DNA"; + m_activeShaderDNA = 0; + return false; + } + } + return true; +} + +void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments) +{ + // Set FBO attachments. These are normally textures, except that on Open GL + // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is + // desired since this cannot be achieved neither with a single texture (not + // before GLES 3.2) nor with separate textures (no suitable format for + // stencil before 3.1 with the appropriate extension). + + QSize fboSize; + GLTextureManager *glTextureManager = m_renderer->nodeManagers()->glTextureManager(); + const auto attachments_ = attachments.attachments(); + for (const Attachment &attachment : attachments_) { + GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid); + if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) { + QOpenGLTexture *glTex = rTex ? rTex->getOrCreateGLTexture() : nullptr; + if (glTex != nullptr) { + if (fboSize.isEmpty()) + fboSize = QSize(glTex->width(), glTex->height()); + else + fboSize = QSize(qMin(fboSize.width(), glTex->width()), qMin(fboSize.height(), glTex->height())); + m_glHelper->bindFrameBufferAttachment(glTex, attachment); + } + } else { + RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr; + if (renderBuffer) { + if (fboSize.isEmpty()) + fboSize = QSize(renderBuffer->width(), renderBuffer->height()); + else + fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()), qMin(fboSize.height(), renderBuffer->height())); + m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment); + } + } + } + m_renderTargetsSize.insert(fboId, fboSize); +} + +void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments) +{ + const QVector<int> activeDrawBuffers = attachments.getGlDrawBuffers(); + + if (m_glHelper->checkFrameBufferComplete()) { + if (activeDrawBuffers.size() > 1) {// We need MRT + if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) { + // Set up MRT, glDrawBuffers... + m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data()); + } + } + } else { + qWarning() << "FBO incomplete"; + } +} + + +void SubmissionContext::setActiveMaterial(Material *rmat) +{ + if (m_material == rmat) + return; + + deactivateTexturesWithScope(TextureScopeMaterial); + m_material = rmat; +} + +int SubmissionContext::activateTexture(TextureScope scope, GLTexture *tex, int onUnit) +{ + // Returns the texture unit to use for the texture + // This always return a valid unit, unless there are more textures than + // texture unit available for the current material + onUnit = assignUnitForTexture(tex); + + // check we didn't overflow the available units + if (onUnit == -1) + return -1; + + // actually re-bind if required, the tex->dna on the unit not being the same + // Note: tex->dna() could be 0 if the texture has not been created yet + if (m_activeTextures[onUnit].texture != tex) { + QOpenGLTexture *glTex = tex->getOrCreateGLTexture(); + if (glTex == nullptr) + return -1; + glTex->bind(onUnit); + m_activeTextures[onUnit].texture = tex; + } + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + int err = m_gl->functions()->glGetError(); + if (err) + qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16) + << tex->textureId() << "on unit" << onUnit; +#endif + + m_activeTextures[onUnit].score = 200; + m_activeTextures[onUnit].pinned = true; + m_activeTextures[onUnit].scope = scope; + + return onUnit; +} + +void SubmissionContext::deactivateTexturesWithScope(TextureScope ts) +{ + for (int u=0; u<m_activeTextures.size(); ++u) { + if (!m_activeTextures[u].pinned) + continue; // inactive, ignore + + if (m_activeTextures[u].scope == ts) { + m_activeTextures[u].pinned = false; + m_activeTextures[u].score = qMax(m_activeTextures[u].score, 1) - 1; + } + } // of units iteration +} + +void SubmissionContext::deactivateTexture(GLTexture* tex) +{ + for (int u=0; u<m_activeTextures.size(); ++u) { + if (m_activeTextures[u].texture == tex) { + Q_ASSERT(m_activeTextures[u].pinned); + m_activeTextures[u].pinned = false; + return; + } + } // of units iteration + + qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex; +} + +/*! + \internal + Returns a texture unit for a texture, -1 if all texture units are assigned. + Tries to use the texture unit with the texture that hasn't been used for the longest time + if the texture happens not to be already pinned on a texture unit. + */ +GLint SubmissionContext::assignUnitForTexture(GLTexture *tex) +{ + int lowestScoredUnit = -1; + int lowestScore = 0xfffffff; + + for (int u=0; u<m_activeTextures.size(); ++u) { + if (m_activeTextures[u].texture == tex) + return u; + + // No texture is currently active on the texture unit + // we save the texture unit with the texture that has been on there + // the longest time while not being used + if (!m_activeTextures[u].pinned) { + int score = m_activeTextures[u].score; + if (score < lowestScore) { + lowestScore = score; + lowestScoredUnit = u; + } + } + } // of units iteration + + if (lowestScoredUnit == -1) + qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!"; + + return lowestScoredUnit; +} + +void SubmissionContext::decayTextureScores() +{ + for (int u = 0; u < m_activeTextures.size(); u++) + m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0); +} + +void SubmissionContext::setCurrentStateSet(RenderStateSet *ss) +{ + if (ss == m_stateSet) + return; + if (ss) + ss->apply(this); + m_stateSet = ss; +} + +RenderStateSet *SubmissionContext::currentStateSet() const +{ + return m_stateSet; +} + +void SubmissionContext::clearColor(const QColor &color) +{ + if (m_currClearColorValue != color) { + m_currClearColorValue = color; + m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + } +} + +void SubmissionContext::clearDepthValue(float depth) +{ + if (m_currClearDepthValue != depth) { + m_currClearDepthValue = depth; + m_gl->functions()->glClearDepthf(depth); + } +} + +void SubmissionContext::clearStencilValue(int stencil) +{ + if (m_currClearStencilValue != stencil) { + m_currClearStencilValue = stencil; + m_gl->functions()->glClearStencil(stencil); + } +} + +// It will be easier if the QGraphicContext applies the QUniformPack +// than the other way around +bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) +{ + // Activate textures and update TextureUniform in the pack + // with the correct textureUnit + + // Set the pinned texture of the previous material texture + // to pinable so that we should easily find an available texture unit + NodeManagers *manager = m_renderer->nodeManagers(); + + deactivateTexturesWithScope(TextureScopeMaterial); + // Update the uniforms with the correct texture unit id's + PackUniformHash &uniformValues = parameterPack.uniforms(); + + for (int i = 0; i < parameterPack.textures().size(); ++i) { + const ShaderParameterPack::NamedTexture &namedTex = parameterPack.textures().at(i); + // Given a Texture QNodeId, we retrieve the associated shared GLTexture + if (uniformValues.contains(namedTex.glslNameId)) { + GLTexture *t = manager->glTextureManager()->lookupResource(namedTex.texId); + if (t != nullptr) { + UniformValue &texUniform = uniformValues[namedTex.glslNameId]; + Q_ASSERT(texUniform.valueType() == UniformValue::TextureValue); + const int texUnit = activateTexture(TextureScopeMaterial, t); + texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit; + if (texUnit == -1) + return false; + } + } + } + + QOpenGLShaderProgram *shader = activeShader(); + + // TO DO: We could cache the binding points somehow and only do the binding when necessary + // for SSBO and UBO + + // Bind Shader Storage block to SSBO and update SSBO + const QVector<BlockToSSBO> blockToSSBOs = parameterPack.shaderStorageBuffers(); + int ssboIndex = 0; + for (const BlockToSSBO b : blockToSSBOs) { + Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); + GLBuffer *ssbo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::ShaderStorageBuffer); + bindShaderStorageBlock(shader->programId(), b.m_blockIndex, ssboIndex); + // Needed to avoid conflict where the buffer would already + // be bound as a VertexArray + bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer); + ssbo->bindBufferBase(this, ssboIndex++, GLBuffer::ShaderStorageBuffer); + // TO DO: Make sure that there's enough binding points + } + + // Bind UniformBlocks to UBO and update UBO from Buffer + // TO DO: Convert ShaderData to Buffer so that we can use that generic process + const QVector<BlockToUBO> blockToUBOs = parameterPack.uniformBuffers(); + int uboIndex = 0; + for (const BlockToUBO &b : blockToUBOs) { + Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); + GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer, GLBuffer::UniformBuffer); + bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex); + // Needed to avoid conflict where the buffer would already + // be bound as a VertexArray + bindGLBuffer(ubo, GLBuffer::UniformBuffer); + ubo->bindBufferBase(this, uboIndex++, GLBuffer::UniformBuffer); + // TO DO: Make sure that there's enough binding points + } + + // Update uniforms in the Default Uniform Block + const PackUniformHash values = parameterPack.uniforms(); + const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms(); + + for (const ShaderUniform &uniform : activeUniforms) { + // We can use [] as we are sure the the uniform wouldn't + // be un activeUniforms if there wasn't a matching value + const UniformValue &v = values[uniform.m_nameId]; + + // skip invalid textures + if (v.valueType() == UniformValue::TextureValue && *v.constData<int>() == -1) + continue; + + applyUniform(uniform, v); + } + // if not all data is valid, the next frame will be rendered immediately + return true; +} + +void SubmissionContext::enableAttribute(const VAOVertexAttribute &attr) +{ + // Bind buffer within the current VAO + GLBuffer *buf = m_renderer->nodeManagers()->glBufferManager()->data(attr.bufferHandle); + Q_ASSERT(buf); + bindGLBuffer(buf, attr.attributeType); + + // Don't use QOpenGLShaderProgram::setAttributeBuffer() because of QTBUG-43199. + // Use the introspection data and set the attribute explicitly + m_glHelper->enableVertexAttributeArray(attr.location); + m_glHelper->vertexAttributePointer(attr.shaderDataType, + attr.location, + attr.vertexSize, + attr.dataType, + GL_TRUE, // TODO: Support normalization property on QAttribute + attr.byteStride, + reinterpret_cast<const void *>(qintptr(attr.byteOffset))); + + + // Done by the helper if it supports it + if (attr.divisor != 0) + m_glHelper->vertexAttribDivisor(attr.location, attr.divisor); +} + +void SubmissionContext::disableAttribute(const SubmissionContext::VAOVertexAttribute &attr) +{ + QOpenGLShaderProgram *prog = activeShader(); + prog->disableAttributeArray(attr.location); +} + +// Note: needs to be called while VAO is bound +void SubmissionContext::specifyAttribute(const Attribute *attribute, + Buffer *buffer, + const ShaderAttribute *attributeDescription) +{ + const int location = attributeDescription->m_location; + if (location < 0) { + qCWarning(Backend) << "failed to resolve location for attribute:" << attribute->name(); + return; + } + + const GLint attributeDataType = glDataTypeFromAttributeDataType(attribute->vertexBaseType()); + const HGLBuffer glBufferHandle = m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()); + Q_ASSERT(!glBufferHandle.isNull()); + const GLBuffer::Type attributeType = attributeTypeToGLBufferType(attribute->attributeType()); + + int typeSize = 0; + int attrCount = 0; + + if (attribute->vertexSize() >= 1 && attribute->vertexSize() <= 4) { + attrCount = 1; + } else if (attribute->vertexSize() == 9) { + typeSize = byteSizeFromType(attributeDataType); + attrCount = 3; + } else if (attribute->vertexSize() == 16) { + typeSize = byteSizeFromType(attributeDataType); + attrCount = 4; + } else { + Q_UNREACHABLE(); + } + + for (int i = 0; i < attrCount; i++) { + VAOVertexAttribute attr; + attr.bufferHandle = glBufferHandle; + attr.attributeType = attributeType; + attr.location = location + i; + attr.dataType = attributeDataType; + attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize); + attr.vertexSize = attribute->vertexSize() / attrCount; + attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize); + attr.divisor = attribute->divisor(); + attr.shaderDataType = attributeDescription->m_type; + + enableAttribute(attr); + + // Save this in the current emulated VAO + if (m_currentVAO) + m_currentVAO->saveVertexAttribute(attr); + } +} + +void SubmissionContext::specifyIndices(Buffer *buffer) +{ + GLBuffer *buf = glBufferForRenderBuffer(buffer, GLBuffer::IndexBuffer); + if (!bindGLBuffer(buf, GLBuffer::IndexBuffer)) + qCWarning(Backend) << Q_FUNC_INFO << "binding index buffer failed"; + + // bound within the current VAO + // Save this in the current emulated VAO + if (m_currentVAO) + m_currentVAO->saveIndexAttribute(m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId())); +} + +void SubmissionContext::updateBuffer(Buffer *buffer) +{ + const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); + if (it != m_renderBufferHash.end()) + uploadDataToGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value())); +} + +QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer) +{ + const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); + if (it != m_renderBufferHash.end()) + return downloadDataFromGLBuffer(buffer, m_renderer->nodeManagers()->glBufferManager()->data(it.value())); + return QByteArray(); +} + +void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId) +{ + auto it = m_renderBufferHash.find(bufferId); + if (it != m_renderBufferHash.end()) { + HGLBuffer glBuffHandle = it.value(); + GLBuffer *glBuff = m_renderer->nodeManagers()->glBufferManager()->data(glBuffHandle); + + Q_ASSERT(glBuff); + // Destroy the GPU resource + glBuff->destroy(this); + // Destroy the GLBuffer instance + m_renderer->nodeManagers()->glBufferManager()->releaseResource(bufferId); + // Remove Id - HGLBuffer entry + m_renderBufferHash.erase(it); + } +} + +bool SubmissionContext::hasGLBufferForBuffer(Buffer *buffer) +{ + const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(buffer->peerId()); + return (it != m_renderBufferHash.end()); +} + +GLBuffer *SubmissionContext::glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type) +{ + if (!m_renderBufferHash.contains(buf->peerId())) + m_renderBufferHash.insert(buf->peerId(), createGLBufferFor(buf, type)); + return m_renderer->nodeManagers()->glBufferManager()->data(m_renderBufferHash.value(buf->peerId())); +} + +HGLBuffer SubmissionContext::createGLBufferFor(Buffer *buffer, GLBuffer::Type type) +{ + GLBuffer *b = m_renderer->nodeManagers()->glBufferManager()->getOrCreateResource(buffer->peerId()); + // b.setUsagePattern(static_cast<QOpenGLBuffer::UsagePattern>(buffer->usage())); + Q_ASSERT(b); + if (!b->create(this)) + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer creation failed"; + + if (!bindGLBuffer(b, type)) + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer binding failed"; + + return m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()); +} + +bool SubmissionContext::bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type) +{ + if (type == GLBuffer::ArrayBuffer && buffer == m_boundArrayBuffer) + return true; + + if (buffer->bind(this, type)) { + if (type == GLBuffer::ArrayBuffer) + m_boundArrayBuffer = buffer; + return true; + } + return false; +} + +void SubmissionContext::uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer) +{ + if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're uploading, the type doesn't matter here + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed"; + // If the buffer is dirty (hence being called here) + // there are two possible cases + // * setData was called changing the whole data or functor (or the usage pattern) + // * partial buffer updates where received + + // TO DO: Handle usage pattern + QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates()); + for (auto it = updates.begin(); it != updates.end(); ++it) { + auto update = it; + // We have a partial update + if (update->offset >= 0) { + //accumulate sequential updates as single one + int bufferSize = update->data.size(); + auto it2 = it + 1; + while ((it2 != updates.end()) + && (it2->offset - update->offset == bufferSize)) { + bufferSize += it2->data.size(); + ++it2; + } + update->data.resize(bufferSize); + while (it + 1 != it2) { + ++it; + update->data.replace(it->offset - update->offset, it->data.size(), it->data); + it->data.clear(); + } + // TO DO: based on the number of updates .., it might make sense to + // sometime use glMapBuffer rather than glBufferSubData + b->update(this, update->data.constData(), update->data.size(), update->offset); + } else { + // We have an update that was done by calling QBuffer::setData + // which is used to resize or entirely clear the buffer + // Note: we use the buffer data directly in that case + const int bufferSize = buffer->data().size(); + b->allocate(this, bufferSize, false); // orphan the buffer + b->allocate(this, buffer->data().constData(), bufferSize, false); + } + } + + if (releaseBuffer) { + b->release(this); + m_boundArrayBuffer = nullptr; + } + qCDebug(Render::Io) << "uploaded buffer size=" << buffer->data().size(); +} + +QByteArray SubmissionContext::downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b) +{ + if (!bindGLBuffer(b, GLBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here + qCWarning(Render::Io) << Q_FUNC_INFO << "buffer bind failed"; + + QByteArray data = b->download(this, buffer->data().size()); + return data; +} + +void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, + Qt3DCore::QNodeId outputRenderTargetId, + QRect inputRect, QRect outputRect, + uint defaultFboId, + QRenderTargetOutput::AttachmentPoint inputAttachmentPoint, + QRenderTargetOutput::AttachmentPoint outputAttachmentPoint, + QBlitFramebuffer::InterpolationMethod interpolationMethod) +{ + GLuint inputFboId = defaultFboId; + bool inputBufferIsDefault = true; + if (!inputRenderTargetId.isNull()) { + RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(inputRenderTargetId); + if (renderTarget) { + AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); + if (m_renderTargets.contains(inputRenderTargetId)) + inputFboId = updateRenderTarget(inputRenderTargetId, attachments, false); + else + inputFboId = createRenderTarget(inputRenderTargetId, attachments); + } + inputBufferIsDefault = false; + } + + GLuint outputFboId = defaultFboId; + bool outputBufferIsDefault = true; + if (!outputRenderTargetId.isNull()) { + RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(outputRenderTargetId); + if (renderTarget) { + AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); + if (m_renderTargets.contains(outputRenderTargetId)) + outputFboId = updateRenderTarget(outputRenderTargetId, attachments, false); + else + outputFboId = createRenderTarget(outputRenderTargetId, attachments); + } + outputBufferIsDefault = false; + } + + // Up until this point the input and output rects are normal Qt rectangles. + // Convert them to GL rectangles (Y at bottom). + const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[inputFboId].height(); + const GLint srcX0 = inputRect.left(); + const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height()); + const GLint srcX1 = srcX0 + inputRect.width(); + const GLint srcY1 = srcY0 + inputRect.height(); + + const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[outputFboId].height(); + const GLint dstX0 = outputRect.left(); + const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height()); + const GLint dstX1 = dstX0 + outputRect.width(); + const GLint dstY1 = dstY0 + outputRect.height(); + + //Get the last bounded framebuffers + const GLuint lastDrawFboId = boundFrameBufferObject(); + + // Activate input framebuffer for reading + bindFramebuffer(inputFboId, GraphicsHelperInterface::FBORead); + + // Activate output framebuffer for writing + bindFramebuffer(outputFboId, GraphicsHelperInterface::FBODraw); + + //Bind texture + if (!inputBufferIsDefault) + readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint); + + if (!outputBufferIsDefault) + drawBuffer(GL_COLOR_ATTACHMENT0 + outputAttachmentPoint); + + // Blit framebuffer + const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR; + m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + GL_COLOR_BUFFER_BIT, mode); + + // Reset draw buffer + bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw); +} + +} // namespace Render +} // namespace Qt3DRender of namespace + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h new file mode 100644 index 000000000..8efdcbc63 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H +#define QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/qclearbuffers.h> +#include <Qt3DRender/private/glbuffer_p.h> +#include <Qt3DRender/qattribute.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/shadercache_p.h> + +QT_BEGIN_NAMESPACE + +class QAbstractOpenGLFunctions; + +namespace Qt3DRender { + +namespace Render { + +class Renderer; +class GraphicsHelperInterface; +class RenderStateSet; +class Material; +class GLTexture; +class RenderCommand; +class RenderTarget; +class AttachmentPack; +class Attribute; +class Buffer; +class ShaderManager; + +enum TextureScope +{ + TextureScopeMaterial = 0, + TextureScopeTechnique + // per-pass for deferred rendering? +}; + +typedef QPair<QString, int> NamedUniformLocation; + +class Q_AUTOTEST_EXPORT SubmissionContext : public GraphicsContext +{ +public: + SubmissionContext(); + ~SubmissionContext(); + + int id() const; // unique, small integer ID of this context + void setRenderer(Renderer *renderer) { m_renderer = renderer; } + + bool beginDrawing(QSurface *surface); + void endDrawing(bool swapBuffers); + void activateGLHelper(); + void releaseOpenGL(); + void setOpenGLContext(QOpenGLContext* ctx); + + // Viewport + void setViewport(const QRectF &viewport, const QSize &surfaceSize); + QRectF viewport() const { return m_viewport; } + + // Shaders + bool activateShader(ProgramDNA shaderDNA); + QOpenGLShaderProgram *activeShader() const { return m_activeShader; } + + // FBO + GLuint activeFBO() const { return m_activeFBO; } + void activateRenderTarget(const Qt3DCore::QNodeId id, const AttachmentPack &attachments, GLuint defaultFboId); + QSize renderTargetSize(const QSize &surfaceSize) const; + QImage readFramebuffer(const QRect &rect); + void blitFramebuffer(Qt3DCore::QNodeId outputRenderTargetId, Qt3DCore::QNodeId inputRenderTargetId, + QRect inputRect, + QRect outputRect, uint defaultFboId, + QRenderTargetOutput::AttachmentPoint inputAttachmentPoint, + QRenderTargetOutput::AttachmentPoint outputAttachmentPoint, + QBlitFramebuffer::InterpolationMethod interpolationMethod); + + + // Material + Material* activeMaterial() const { return m_material; } + void setActiveMaterial(Material* rmat); + + // Attributes + void specifyAttribute(const Attribute *attribute, + Buffer *buffer, + const ShaderAttribute *attributeDescription); + void specifyIndices(Buffer *buffer); + + // Buffer + void updateBuffer(Buffer *buffer); + QByteArray downloadBufferContent(Buffer *buffer); + void releaseBuffer(Qt3DCore::QNodeId bufferId); + bool hasGLBufferForBuffer(Buffer *buffer); + GLBuffer *glBufferForRenderBuffer(Buffer *buf, GLBuffer::Type type); + + // Parameters + bool setParameters(ShaderParameterPack ¶meterPack); + + // Textures + int activateTexture(TextureScope scope, GLTexture* tex, int onUnit = -1); + void deactivateTexture(GLTexture *tex); + + // RenderState + void setCurrentStateSet(RenderStateSet* ss); + RenderStateSet *currentStateSet() const; + + // Wrappers + void clearColor(const QColor &color); + void clearDepthValue(float depth); + void clearStencilValue(int stencil); + +private: + void initialize(); + + // Textures + void decayTextureScores(); + GLint assignUnitForTexture(GLTexture* tex); + void deactivateTexturesWithScope(TextureScope ts); + + // FBO + void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments); + void activateDrawBuffers(const AttachmentPack &attachments); + void resolveRenderTargetFormat(); + GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments); + GLuint updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget); + + + // Buffers + HGLBuffer createGLBufferFor(Buffer *buffer, GLBuffer::Type type); + void uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer = false); + QByteArray downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b); + bool bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type); + + bool m_initialized; + bool m_ownCurrent; + const unsigned int m_id; + QSurface *m_surface; + QSize m_surfaceSize; + + QOpenGLShaderProgram *m_activeShader; + ProgramDNA m_activeShaderDNA; + + QHash<Qt3DCore::QNodeId, HGLBuffer> m_renderBufferHash; + QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets; + QHash<GLuint, QSize> m_renderTargetsSize; + QAbstractTexture::TextureFormat m_renderTargetFormat; + + QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers; + + // active textures, indexed by texture unit + struct ActiveTexture { + GLTexture *texture = nullptr; + int score = 0; + TextureScope scope = TextureScopeMaterial; + bool pinned = false; + }; + QVector<ActiveTexture> m_activeTextures; + + // cache some current state, to make sure we don't issue unnecessary GL calls + int m_currClearStencilValue; + float m_currClearDepthValue; + QColor m_currClearColorValue; + + Material* m_material; + QRectF m_viewport; + GLuint m_activeFBO; + + GLBuffer *m_boundArrayBuffer; + RenderStateSet* m_stateSet; + Renderer *m_renderer; + QByteArray m_uboTempArray; + + + // Attributes + friend class OpenGLVertexArrayObject; + OpenGLVertexArrayObject *m_currentVAO; + + struct VAOVertexAttribute + { + HGLBuffer bufferHandle; + GLBuffer::Type attributeType; + int location; + GLint dataType; + uint byteOffset; + uint vertexSize; + uint byteStride; + uint divisor; + GLenum shaderDataType; + }; + + using VAOIndexAttribute = HGLBuffer; + void enableAttribute(const VAOVertexAttribute &attr); + void disableAttribute(const VAOVertexAttribute &attr); +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_SUBMISSIONCONTEXT_H diff --git a/src/render/renderers/opengl/io/glbuffer.cpp b/src/render/renderers/opengl/io/glbuffer.cpp new file mode 100644 index 000000000..f1b860f03 --- /dev/null +++ b/src/render/renderers/opengl/io/glbuffer.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "glbuffer_p.h" +#include <private/graphicscontext_p.h> + +#if !defined(GL_UNIFORM_BUFFER) +#define GL_UNIFORM_BUFFER 0x8A11 +#endif +#if !defined(GL_ARRAY_BUFFER) +#define GL_ARRAY_BUFFER 0x8892 +#endif +#if !defined(GL_ELEMENT_ARRAY_BUFFER) +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#endif +#if !defined(GL_SHADER_STORAGE_BUFFER) +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#endif +#if !defined(GL_PIXEL_PACK_BUFFER) +#define GL_PIXEL_PACK_BUFFER 0x88EB +#endif +#if !defined(GL_PIXEL_UNPACK_BUFFER) +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#endif +#if !defined(GL_DRAW_INDIRECT_BUFFER) +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#endif + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +// A UBO is created for each ShaderData Shader Pair +// That means a UBO is unique to a shader/shaderdata + +namespace { + +GLenum glBufferTypes[] = { + GL_ARRAY_BUFFER, + GL_UNIFORM_BUFFER, + GL_ELEMENT_ARRAY_BUFFER, + GL_SHADER_STORAGE_BUFFER, + GL_PIXEL_PACK_BUFFER, + GL_PIXEL_UNPACK_BUFFER, + GL_DRAW_INDIRECT_BUFFER +}; + +} // anonymous + +GLBuffer::GLBuffer() + : m_bufferId(0) + , m_isCreated(false) + , m_bound(false) + , m_lastTarget(GL_ARRAY_BUFFER) +{ +} + +bool GLBuffer::bind(GraphicsContext *ctx, Type t) +{ + if (m_bufferId == 0) + return false; + m_lastTarget = glBufferTypes[t]; + ctx->openGLContext()->functions()->glBindBuffer(m_lastTarget, m_bufferId); + m_bound = true; + return true; +} + +bool GLBuffer::release(GraphicsContext *ctx) +{ + m_bound = false; + ctx->openGLContext()->functions()->glBindBuffer(m_lastTarget, 0); + return true; +} + +bool GLBuffer::create(GraphicsContext *ctx) +{ + ctx->openGLContext()->functions()->glGenBuffers(1, &m_bufferId); + m_isCreated = true; + return m_bufferId != 0; +} + +void GLBuffer::destroy(GraphicsContext *ctx) +{ + ctx->openGLContext()->functions()->glDeleteBuffers(1, &m_bufferId); + m_isCreated = false; +} + +void GLBuffer::allocate(GraphicsContext *ctx, uint size, bool dynamic) +{ + // Either GL_STATIC_DRAW OR GL_DYNAMIC_DRAW depending on the use case + // TO DO: find a way to know how a buffer/QShaderData will be used to use the right usage + ctx->openGLContext()->functions()->glBufferData(m_lastTarget, size, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); +} + +void GLBuffer::allocate(GraphicsContext *ctx, const void *data, uint size, bool dynamic) +{ + ctx->openGLContext()->functions()->glBufferData(m_lastTarget, size, data, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); +} + +void GLBuffer::update(GraphicsContext *ctx, const void *data, uint size, int offset) +{ + ctx->openGLContext()->functions()->glBufferSubData(m_lastTarget, offset, size, data); +} + +QByteArray GLBuffer::download(GraphicsContext *ctx, uint size) +{ + char *gpu_ptr = ctx->mapBuffer(m_lastTarget, size); + QByteArray data; + if (gpu_ptr != nullptr) { + data.resize(size); + std::copy(gpu_ptr, gpu_ptr+size, data.data()); + } + ctx->unmapBuffer(m_lastTarget); + return data; +} + +void GLBuffer::bindBufferBase(GraphicsContext *ctx, int bindingPoint, GLBuffer::Type t) +{ + ctx->bindBufferBase(glBufferTypes[t], bindingPoint, m_bufferId); +} + +void GLBuffer::bindBufferBase(GraphicsContext *ctx, int bindingPoint) +{ + ctx->bindBufferBase(m_lastTarget, bindingPoint, m_bufferId); +} + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/io/glbuffer_p.h b/src/render/renderers/opengl/io/glbuffer_p.h new file mode 100644 index 000000000..731634b6b --- /dev/null +++ b/src/render/renderers/opengl/io/glbuffer_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GLBUFFER_P_H +#define QT3DRENDER_RENDER_GLBUFFER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QOpenGLContext> +#include <Qt3DCore/qnodeid.h> +#include <qbytearray.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class GraphicsContext; + +class GLBuffer +{ +public: + GLBuffer(); + + enum Type + { + ArrayBuffer = 0, + UniformBuffer, + IndexBuffer, + ShaderStorageBuffer, + PixelPackBuffer, + PixelUnpackBuffer, + DrawIndirectBuffer + }; + + bool bind(GraphicsContext *ctx, Type t); + bool release(GraphicsContext *ctx); + bool create(GraphicsContext *ctx); + void destroy(GraphicsContext *ctx); + void allocate(GraphicsContext *ctx, uint size, bool dynamic = true); + void allocate(GraphicsContext *ctx, const void *data, uint size, bool dynamic = true); + void update(GraphicsContext *ctx, const void *data, uint size, int offset = 0); + QByteArray download(GraphicsContext *ctx, uint size); + void bindBufferBase(GraphicsContext *ctx, int bindingPoint, Type t); + void bindBufferBase(GraphicsContext *ctx, int bindingPoint); + + inline GLuint bufferId() const { return m_bufferId; } + inline bool isCreated() const { return m_isCreated; } + inline bool isBound() const { return m_bound; } + +private: + GLuint m_bufferId; + bool m_isCreated; + bool m_bound; + GLenum m_lastTarget; +}; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLBUFFER_P_H diff --git a/src/render/renderers/opengl/io/io.pri b/src/render/renderers/opengl/io/io.pri new file mode 100644 index 000000000..462978c4d --- /dev/null +++ b/src/render/renderers/opengl/io/io.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/glbuffer.cpp + +HEADERS += \ + $$PWD/glbuffer_p.h + diff --git a/src/render/renderers/opengl/jobs/filtercompatibletechniquejob.cpp b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob.cpp new file mode 100644 index 000000000..342fd3dad --- /dev/null +++ b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filtercompatibletechniquejob_p.h" +#include <Qt3DRender/private/techniquemanager_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/job_common_p.h> +#include <Qt3DRender/private/submissioncontext_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +FilterCompatibleTechniqueJob::FilterCompatibleTechniqueJob() + : m_manager(nullptr) + , m_renderer(nullptr) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::FilterCompatibleTechniques, 0); +} + +void FilterCompatibleTechniqueJob::setManager(TechniqueManager *manager) +{ + m_manager = manager; +} + +TechniqueManager *FilterCompatibleTechniqueJob::manager() const +{ + return m_manager; +} + +void FilterCompatibleTechniqueJob::setRenderer(Renderer *renderer) +{ + m_renderer = renderer; +} + +Renderer *FilterCompatibleTechniqueJob::renderer() const +{ + return m_renderer; +} + +void FilterCompatibleTechniqueJob::run() +{ + Q_ASSERT(m_manager != nullptr && m_renderer != nullptr); + Q_ASSERT(m_renderer->isRunning() && m_renderer->submissionContext()->isInitialized()); + + const QVector<Qt3DCore::QNodeId> dirtyTechniqueIds = m_manager->takeDirtyTechniques(); + for (const Qt3DCore::QNodeId techniqueId : dirtyTechniqueIds) { + Technique *technique = m_manager->lookupResource(techniqueId); + if (Q_LIKELY(technique != nullptr)) + technique->setCompatibleWithRenderer((*m_renderer->contextInfo() == *technique->graphicsApiFilter())); + } +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/jobs/filtercompatibletechniquejob_p.h b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob_p.h new file mode 100644 index 000000000..4f7a7146c --- /dev/null +++ b/src/render/renderers/opengl/jobs/filtercompatibletechniquejob_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_FILTERCOMPATIBLETECHNIQUEJOB_H +#define QT3DRENDER_RENDER_FILTERCOMPATIBLETECHNIQUEJOB_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/qt3drender_global_p.h> + +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class TechniqueManager; +class Renderer; + +class QT3DRENDERSHARED_PRIVATE_EXPORT FilterCompatibleTechniqueJob : public Qt3DCore::QAspectJob +{ +public: + FilterCompatibleTechniqueJob(); + + void setManager(TechniqueManager *managers); + TechniqueManager *manager() const; + + void setRenderer(Renderer *renderer); + Renderer *renderer() const; + + void run() override; + +private: + TechniqueManager *m_manager; + Renderer *m_renderer; +}; + +typedef QSharedPointer<FilterCompatibleTechniqueJob> FilterCompatibleTechniqueJobPtr; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_FILTERCOMPATIBLETECHNIQUEJOB_H diff --git a/src/render/renderers/opengl/jobs/jobs.pri b/src/render/renderers/opengl/jobs/jobs.pri new file mode 100644 index 000000000..021cd3242 --- /dev/null +++ b/src/render/renderers/opengl/jobs/jobs.pri @@ -0,0 +1,15 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/filtercompatibletechniquejob.cpp \ + $$PWD/materialparametergathererjob.cpp \ + $$PWD/renderviewbuilderjob.cpp \ + $$PWD/renderviewinitializerjob.cpp \ + $$PWD/renderviewjobutils.cpp + +HEADERS += \ + $$PWD/filtercompatibletechniquejob_p.h \ + $$PWD/materialparametergathererjob_p.h \ + $$PWD/renderviewbuilderjob_p.h \ + $$PWD/renderviewinitializerjob_p.h \ + $$PWD/renderviewjobutils_p.h diff --git a/src/render/renderers/opengl/jobs/materialparametergathererjob.cpp b/src/render/renderers/opengl/jobs/materialparametergathererjob.cpp new file mode 100644 index 000000000..bae516c7e --- /dev/null +++ b/src/render/renderers/opengl/jobs/materialparametergathererjob.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Paul Lemire +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "materialparametergathererjob_p.h" +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/renderpassfilternode_p.h> +#include <Qt3DRender/private/techniquefilternode_p.h> +#include <Qt3DRender/private/job_common_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +namespace { + +int materialParameterGathererCounter = 0; +const int likelyNumberOfParameters = 24; + +} // anonymous + +MaterialParameterGathererJob::MaterialParameterGathererJob() + : Qt3DCore::QAspectJob() + , m_manager(nullptr) + , m_techniqueFilter(nullptr) + , m_renderPassFilter(nullptr) + , m_renderer(nullptr) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::MaterialParameterGathering, materialParameterGathererCounter++); +} + +// TechniqueFilter / RenderPassFilter + +// Parameters from Material/Effect/Technique + +// Note: we could maybe improve that by having a smart update when we detect +// that a parameter value has changed. That might require way more book keeping +// which might make this solution a bit too complex + +// The fact that this can now be performed in parallel should already provide a big +// improvement +void MaterialParameterGathererJob::run() +{ + for (const HMaterial &materialHandle : qAsConst(m_handles)) { + Material *material = m_manager->materialManager()->data(materialHandle); + + if (Q_UNLIKELY(!material->isEnabled())) + continue; + + Effect *effect = m_manager->effectManager()->lookupResource(material->effect()); + Technique *technique = findTechniqueForEffect(m_renderer, m_techniqueFilter, effect); + + if (Q_LIKELY(technique != nullptr)) { + RenderPassList passes = findRenderPassesForTechnique(m_manager, m_renderPassFilter, technique); + if (Q_LIKELY(passes.size() > 0)) { + // Order set: + // 1 Pass Filter + // 2 Technique Filter + // 3 Material + // 4 Effect + // 5 Technique + // 6 RenderPass + + // Add Parameters define in techniqueFilter and passFilter + // passFilter have priority over techniqueFilter + + ParameterInfoList parameters; + // Doing the reserve allows a gain of 0.5ms on some of the demo examples + parameters.reserve(likelyNumberOfParameters); + + if (m_renderPassFilter) + parametersFromParametersProvider(¶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/render/renderers/opengl/jobs/materialparametergathererjob_p.h b/src/render/renderers/opengl/jobs/materialparametergathererjob_p.h new file mode 100644 index 000000000..687163387 --- /dev/null +++ b/src/render/renderers/opengl/jobs/materialparametergathererjob_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Paul Lemire +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_MATERIALPARAMETERGATHERERJOB_P_H +#define QT3DRENDER_RENDER_MATERIALPARAMETERGATHERERJOB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DCore/qnodeid.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/renderviewjobutils_p.h> +#include <Qt3DRender/private/qt3drender_global_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class NodeManagers; +class TechniqueFilter; +class RenderPassFilter; +class Renderer; + +// TO be executed for each FrameGraph branch with a given RenderPassFilter/TechniqueFilter + +class QT3DRENDERSHARED_PRIVATE_EXPORT MaterialParameterGathererJob : public Qt3DCore::QAspectJob +{ +public: + MaterialParameterGathererJob(); + + inline void setNodeManagers(NodeManagers *manager) Q_DECL_NOTHROW { m_manager = manager; } + inline void setTechniqueFilter(TechniqueFilter *techniqueFilter) Q_DECL_NOTHROW { m_techniqueFilter = techniqueFilter; } + inline void setRenderPassFilter(RenderPassFilter *renderPassFilter) Q_DECL_NOTHROW { m_renderPassFilter = renderPassFilter; } + inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; } + inline const MaterialParameterGathererData &materialToPassAndParameter() Q_DECL_NOTHROW { return m_parameters; } + inline void setHandles(const QVector<HMaterial> &handles) Q_DECL_NOTHROW { m_handles = handles; } + + inline TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_techniqueFilter; } + inline RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_renderPassFilter; } + + void run() final; + +private: + NodeManagers *m_manager; + TechniqueFilter *m_techniqueFilter; + RenderPassFilter *m_renderPassFilter; + Renderer *m_renderer; + + // Material id to array of RenderPasse with parameters + MaterialParameterGathererData m_parameters; + QVector<HMaterial> m_handles; +}; + +typedef QSharedPointer<MaterialParameterGathererJob> MaterialParameterGathererJobPtr; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_MATERIALPARAMETERGATHERERJOB_P_H diff --git a/src/render/renderers/opengl/jobs/renderviewbuilderjob.cpp b/src/render/renderers/opengl/jobs/renderviewbuilderjob.cpp new file mode 100644 index 000000000..fa6218d6f --- /dev/null +++ b/src/render/renderers/opengl/jobs/renderviewbuilderjob.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Paul Lemire +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderviewbuilderjob_p.h" +#include <Qt3DRender/private/job_common_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/renderview_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +namespace { +int renderViewInstanceCounter = 0; +} // anonymous + +RenderViewBuilderJob::RenderViewBuilderJob() + : Qt3DCore::QAspectJob(), + m_renderView(nullptr) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderViewBuilder, renderViewInstanceCounter++); +} + +void RenderViewBuilderJob::run() +{ + // Build RenderCommand should perform the culling as we have no way to determine + // if a child has a mesh in the view frustum while its parent isn't contained in it. + if (!m_renderView->noDraw()) { +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) + gatherLightsTime = timer.nsecsElapsed(); + timer.restart(); +#endif + if (!m_renderView->isCompute()) + m_commands = m_renderView->buildDrawRenderCommands(m_renderables); + else + m_commands = m_renderView->buildComputeRenderCommands(m_renderables); +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) + buildCommandsTime = timer.nsecsElapsed(); + timer.restart(); +#endif + } + +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) + qint64 creationTime = timer.nsecsElapsed(); + timer.restart(); +#endif + +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) + qint64 sortTime = timer.nsecsElapsed(); +#endif + +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) + qDebug() << m_index + << "state:" << gatherStateTime / 1.0e6 + << "lights:" << gatherLightsTime / 1.0e6 + << "build commands:" << buildCommandsTime / 1.0e6 + << "sort:" << sortTime / 1.0e6; +#endif + + +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/jobs/renderviewbuilderjob_p.h b/src/render/renderers/opengl/jobs/renderviewbuilderjob_p.h new file mode 100644 index 000000000..c38f788b0 --- /dev/null +++ b/src/render/renderers/opengl/jobs/renderviewbuilderjob_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Paul Lemire +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERVIEWBUILDERJOB_P_H +#define QT3DRENDER_RENDER_RENDERVIEWBUILDERJOB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/handle_types_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class RenderView; +class Renderer; +class RenderCommand; + +class Q_AUTOTEST_EXPORT RenderViewBuilderJob : public Qt3DCore::QAspectJob +{ +public: + RenderViewBuilderJob(); + + inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; } + inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; } + inline void setIndex(int index) Q_DECL_NOTHROW { m_index = index; } + inline void setRenderables(const QVector<Entity *> &renderables) Q_DECL_NOTHROW { m_renderables = renderables; } + QVector<RenderCommand *> &commands() Q_DECL_NOTHROW { return m_commands; } + + void run() final; + +private: + RenderView *m_renderView; + Renderer *m_renderer; + int m_index; + QVector<Entity *> m_renderables; + QVector<RenderCommand *> m_commands; +}; + +typedef QSharedPointer<RenderViewBuilderJob> RenderViewBuilderJobPtr; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEWBUILDERJOB_P_H diff --git a/src/render/renderers/opengl/jobs/renderviewinitializerjob.cpp b/src/render/renderers/opengl/jobs/renderviewinitializerjob.cpp new file mode 100644 index 000000000..7bf55be40 --- /dev/null +++ b/src/render/renderers/opengl/jobs/renderviewinitializerjob.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 Paul Lemire +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderviewinitializerjob_p.h" + +#include <Qt3DRender/private/renderview_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/renderviewjobutils_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/job_common_p.h> + +#include <QElapsedTimer> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +namespace { +// only accessed in ctor and dtor of RenderViewJob +// which are always being called in a non concurrent manner +int renderViewInstanceCounter = 0; +} // anonymous + +RenderViewInitializerJob::RenderViewInitializerJob() + : m_renderer(nullptr) + , m_fgLeaf(nullptr) + , m_index(0) + , m_renderView(nullptr) +{ + SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderView, renderViewInstanceCounter++); +} + +RenderViewInitializerJob::~RenderViewInitializerJob() +{ + renderViewInstanceCounter--; +} + +void RenderViewInitializerJob::run() +{ + qCDebug(Jobs) << Q_FUNC_INFO << m_index; +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) + QElapsedTimer timer; + timer.start(); + qint64 gatherLightsTime; + qint64 buildCommandsTime; +#endif + + // Create a RenderView object + // The RenderView are created from a QFrameAllocator stored in the current Thread local storage + m_renderView = new RenderView; + + // RenderView should allocate heap resources using only the currentFrameAllocator + m_renderView->setRenderer(m_renderer); + + // Populate the renderview's configuration from the framegraph + setRenderViewConfigFromFrameGraphLeafNode(m_renderView, m_fgLeaf); +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) + qint64 gatherStateTime = timer.nsecsElapsed(); + timer.restart(); +#endif +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/jobs/renderviewinitializerjob_p.h b/src/render/renderers/opengl/jobs/renderviewinitializerjob_p.h new file mode 100644 index 000000000..fb4e2c67c --- /dev/null +++ b/src/render/renderers/opengl/jobs/renderviewinitializerjob_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Paul Lemire +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERVIEWINITIALIZERJOB_H +#define QT3DRENDER_RENDER_RENDERVIEWINITIALIZERJOB_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DCore/private/qframeallocator_p.h> +#include <QSize> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Renderer; +class FrameGraphNode; +class RenderView; + +class Q_AUTOTEST_EXPORT RenderViewInitializerJob : public Qt3DCore::QAspectJob +{ +public: + RenderViewInitializerJob(); + ~RenderViewInitializerJob(); + + inline void setRenderer(Renderer *renderer) { m_renderer = renderer; } + inline RenderView *renderView() const Q_DECL_NOTHROW { return m_renderView; } + + inline void setFrameGraphLeafNode(FrameGraphNode *fgLeaf) + { + m_fgLeaf = fgLeaf; + } + + // Sets the position in the queue of RenderViews that the + // RenderView generated by this job should be inserted. This is + // used to ensure that for example a RenderView for creating + // a shadow map texture is submitted before the RenderView that + // contains commands making use of the shadow map + inline void setSubmitOrderIndex(int index) { m_index = index; } + inline int submitOrderIndex() const { return m_index; } + + void run() override; + +private: + Renderer *m_renderer; + FrameGraphNode *m_fgLeaf; + int m_index; + RenderView *m_renderView; +}; + +typedef QSharedPointer<RenderViewInitializerJob> RenderViewInitializerJobPtr; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEWINITIALIZERJOB_H diff --git a/src/render/renderers/opengl/jobs/renderviewjobutils.cpp b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp new file mode 100644 index 000000000..ffad387c6 --- /dev/null +++ b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderviewjobutils_p.h" +#include "renderlogging_p.h" + +#include <Qt3DRender/qgraphicsapifilter.h> +#include <Qt3DRender/private/sphere_p.h> +#include <Qt3DRender/qshaderdata.h> + +#include <Qt3DRender/private/cameraselectornode_p.h> +#include <Qt3DRender/private/clearbuffers_p.h> +#include <Qt3DRender/private/layerfilternode_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/effect_p.h> +#include <Qt3DRender/private/renderpassfilternode_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/rendertargetselectornode_p.h> +#include <Qt3DRender/private/renderview_p.h> +#include <Qt3DRender/private/sortpolicy_p.h> +#include <Qt3DRender/private/techniquefilternode_p.h> +#include <Qt3DRender/private/viewportnode_p.h> +#include <Qt3DRender/private/shadervariables_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/shaderdata_p.h> +#include <Qt3DRender/private/statesetnode_p.h> +#include <Qt3DRender/private/dispatchcompute_p.h> +#include <Qt3DRender/private/rendersurfaceselector_p.h> +#include <Qt3DRender/private/rendercapture_p.h> +#include <Qt3DRender/private/buffercapture_p.h> +#include <Qt3DRender/private/stringtoint_p.h> +#include <Qt3DRender/private/techniquemanager_p.h> +#include <Qt3DRender/private/memorybarrier_p.h> +#include <Qt3DRender/private/blitframebuffer_p.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt3DCore; + +namespace Qt3DRender { +namespace Render { + +/*! + \internal + Walks up the framegraph tree from \a fgLeaf and builds up as much state + as possible and populates \a rv. For cases where we can't get the specific state + (e.g. because it depends upon more than just the framegraph) we store the data from + the framegraph that will be needed to later when the rest of the data becomes available +*/ +void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf) +{ + // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter + // which is referenced by the Material which is referenced by the RenderMesh. So we can + // only store the filter info in the RenderView structure and use it to do the resolving + // when we build the RenderCommand list. + const NodeManagers *manager = rv->nodeManagers(); + const FrameGraphNode *node = fgLeaf; + + while (node) { + FrameGraphNode::FrameGraphNodeType type = node->nodeType(); + if (node->isEnabled()) + switch (type) { + case FrameGraphNode::InvalidNodeType: + // A base FrameGraphNode, can be used for grouping purposes + break; + case FrameGraphNode::CameraSelector: + // Can be set only once and we take camera nearest to the leaf node + if (!rv->renderCameraLens()) { + const CameraSelector *cameraSelector = static_cast<const CameraSelector *>(node); + Entity *camNode = manager->renderNodesManager()->lookupResource(cameraSelector->cameraUuid()); + if (camNode) { + CameraLens *lens = camNode->renderComponent<CameraLens>(); + rv->setRenderCameraEntity(camNode); + if (lens && lens->isEnabled()) { + rv->setRenderCameraLens(lens); + // ViewMatrix and ProjectionMatrix are computed + // later in updateMatrices() + // since at this point the transformation matrices + // may not yet have been updated + } + } + } + break; + + case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree + rv->appendLayerFilter(static_cast<const LayerFilterNode *>(node)->peerId()); + break; + + case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree + rv->appendProximityFilterId(node->peerId()); + break; + + case FrameGraphNode::RenderPassFilter: + // Can be set once + // TODO: Amalgamate all render pass filters from leaf to root + if (!rv->renderPassFilter()) + rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node)); + break; + + case FrameGraphNode::RenderTarget: { + // Can be set once and we take render target nearest to the leaf node + const RenderTargetSelector *targetSelector = static_cast<const RenderTargetSelector *>(node); + QNodeId renderTargetUid = targetSelector->renderTargetUuid(); + HTarget renderTargetHandle = manager->renderTargetManager()->lookupHandle(renderTargetUid); + + // Add renderTarget Handle and build renderCommand AttachmentPack + if (!rv->renderTargetId()) { + rv->setRenderTargetId(renderTargetUid); + + RenderTarget *renderTarget = manager->renderTargetManager()->data(renderTargetHandle); + if (renderTarget) + rv->setAttachmentPack(AttachmentPack(renderTarget, manager->attachmentManager(), targetSelector->outputs())); + } + break; + } + + case FrameGraphNode::ClearBuffers: { + const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node); + rv->addClearBuffers(cbNode); + break; + } + + case FrameGraphNode::TechniqueFilter: + // Can be set once + // TODO Amalgamate all technique filters from leaf to root + if (!rv->techniqueFilter()) + rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node)); + break; + + case FrameGraphNode::Viewport: { + // If the Viewport has already been set in a lower node + // Make it so that the new viewport is actually + // a subregion relative to that of the parent viewport + const ViewportNode *vpNode = static_cast<const ViewportNode *>(node); + rv->setViewport(computeViewport(rv->viewport(), vpNode)); + rv->setGamma(vpNode->gamma()); + break; + } + + case FrameGraphNode::SortMethod: { + const Render::SortPolicy *sortPolicy = static_cast<const Render::SortPolicy *>(node); + rv->addSortType(sortPolicy->sortTypes()); + break; + } + + case FrameGraphNode::SubtreeSelector: + // Has no meaning here. SubtreeSelector was used + // in a prior step to build the list of RenderViewJobs + break; + + case FrameGraphNode::StateSet: { + const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node); + // Create global RenderStateSet for renderView if no stateSet was set before + RenderStateSet *stateSet = rv->stateSet(); + if (stateSet == nullptr && rStateSet->hasRenderStates()) { + stateSet = new RenderStateSet(); + rv->setStateSet(stateSet); + } + + if (rStateSet->hasRenderStates()) + addToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager()); + break; + } + + case FrameGraphNode::NoDraw: { + rv->setNoDraw(true); + break; + } + + case FrameGraphNode::FrustumCulling: { + rv->setFrustumCulling(true); + break; + } + + case FrameGraphNode::ComputeDispatch: { + const Render::DispatchCompute *dispatchCompute = static_cast<const Render::DispatchCompute *>(node); + rv->setCompute(true); + rv->setComputeWorkgroups(dispatchCompute->x(), + dispatchCompute->y(), + dispatchCompute->z()); + break; + } + + case FrameGraphNode::Lighting: { + // TODO + break; + } + + case FrameGraphNode::Surface: { + // Use the surface closest to leaf node + if (rv->surface() == nullptr) { + const Render::RenderSurfaceSelector *surfaceSelector + = static_cast<const Render::RenderSurfaceSelector *>(node); + rv->setSurface(surfaceSelector->surface()); + rv->setSurfaceSize(surfaceSelector->renderTargetSize() * surfaceSelector->devicePixelRatio()); + } + break; + } + case FrameGraphNode::RenderCapture: { + auto *renderCapture = const_cast<Render::RenderCapture *>( + static_cast<const Render::RenderCapture *>(node)); + if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) { + rv->setRenderCaptureNodeId(renderCapture->peerId()); + rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest()); + } + break; + } + + case FrameGraphNode::MemoryBarrier: { + const Render::MemoryBarrier *barrier = static_cast<const Render::MemoryBarrier *>(node); + rv->setMemoryBarrier(barrier->waitOperations()|rv->memoryBarrier()); + break; + } + + case FrameGraphNode::BufferCapture: { + auto *bufferCapture = const_cast<Render::BufferCapture *>( + static_cast<const Render::BufferCapture *>(node)); + if (bufferCapture != nullptr) + rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled()); + break; + } + + case FrameGraphNode::BlitFramebuffer: { + const Render::BlitFramebuffer *blitFramebufferNode = + static_cast<const Render::BlitFramebuffer *>(node); + rv->setHasBlitFramebufferInfo(true); + BlitFramebufferInfo bfbInfo; + bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId(); + bfbInfo.destinationRenderTargetId = blitFramebufferNode->destinationRenderTargetId(); + bfbInfo.sourceRect = blitFramebufferNode->sourceRect(); + bfbInfo.destinationRect = blitFramebufferNode->destinationRect(); + bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint(); + bfbInfo.destinationAttachmentPoint = blitFramebufferNode->destinationAttachmentPoint(); + bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod(); + rv->setBlitFrameBufferInfo(bfbInfo); + break; + } + + default: + // Should never get here + qCWarning(Backend) << "Unhandled FrameGraphNode type"; + } + + node = node->parent(); + } +} + +/*! + \internal + Searches the \a renderer for the best matching Technique from + \a effect specified by the \a renderView. +*/ +Technique *findTechniqueForEffect(Renderer *renderer, + const TechniqueFilter *techniqueFilter, + Effect *effect) +{ + if (!effect) + return nullptr; + + NodeManagers *manager = renderer->nodeManagers(); + QVector<Technique*> matchingTechniques; + const bool hasInvalidTechniqueFilter = (techniqueFilter == nullptr || techniqueFilter->filters().isEmpty()); + + // Iterate through the techniques in the effect + const auto techniqueIds = effect->techniques(); + for (const QNodeId techniqueId : techniqueIds) { + Technique *technique = manager->techniqueManager()->lookupResource(techniqueId); + + // Should be valid, if not there likely a problem with node addition/destruction changes + Q_ASSERT(technique); + + // Check if the technique is compatible with the rendering API + // If no techniqueFilter is present, we return the technique as it satisfies OpenGL version + if (technique->isCompatibleWithRenderer() && (hasInvalidTechniqueFilter || technique->isCompatibleWithFilters(techniqueFilter->filters()))) + matchingTechniques.append(technique); + } + + if (matchingTechniques.size() == 0) // We failed to find a suitable technique to use :( + return nullptr; + + if (matchingTechniques.size() == 1) + return matchingTechniques.first(); + + // Several compatible techniques, return technique with highest major and minor version + Technique* highest = matchingTechniques.first(); + GraphicsApiFilterData filter = *highest->graphicsApiFilter(); + for (auto it = matchingTechniques.cbegin() + 1; it < matchingTechniques.cend(); ++it) { + if (filter < *(*it)->graphicsApiFilter()) { + filter = *(*it)->graphicsApiFilter(); + highest = *it; + } + } + return highest; +} + + +RenderPassList findRenderPassesForTechnique(NodeManagers *manager, + const RenderPassFilter *passFilter, + Technique *technique) +{ + Q_ASSERT(manager); + Q_ASSERT(technique); + + RenderPassList passes; + const auto passIds = technique->renderPasses(); + for (const QNodeId passId : passIds) { + RenderPass *renderPass = manager->renderPassManager()->lookupResource(passId); + + if (renderPass && renderPass->isEnabled()) { + bool foundMatch = (!passFilter || passFilter->filters().size() == 0); + + // A pass filter is present so we need to check for matching criteria + if (!foundMatch && renderPass->filterKeys().size() >= passFilter->filters().size()) { + + // Iterate through the filter criteria and look for render passes with criteria that satisfy them + const auto filterKeyIds = passFilter->filters(); + for (const QNodeId filterKeyId : filterKeyIds) { + foundMatch = false; + FilterKey *filterFilterKey = manager->filterKeyManager()->lookupResource(filterKeyId); + + const auto passFilterKeyIds = renderPass->filterKeys(); + for (const QNodeId passFilterKeyId : passFilterKeyIds) { + FilterKey *passFilterKey = manager->filterKeyManager()->lookupResource(passFilterKeyId); + if ((foundMatch = (*passFilterKey == *filterFilterKey))) + break; + } + + if (!foundMatch) { + // No match for criterion in any of the render pass' criteria + break; + } + } + } + + if (foundMatch) { + // Found a renderpass that satisfies our needs. Add it in order + passes << renderPass; + } + } + } + + return passes; +} + + +ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *params, const int nameId) +{ + const ParameterInfoList::const_iterator end = params->cend(); + ParameterInfoList::const_iterator it = std::lower_bound(params->cbegin(), end, nameId); + if (it != end && it->nameId != nameId) + return end; + return it; +} + +void addParametersForIds(ParameterInfoList *params, ParameterManager *manager, + const Qt3DCore::QNodeIdVector ¶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) Technique + // 3) Effect + // + // That way a user can override defaults in Effect's and Techniques on a + // object manner and a Technique can override global defaults from the Effect. + parametersFromParametersProvider(infoList, manager, material); + parametersFromParametersProvider(infoList, manager, technique); + parametersFromParametersProvider(infoList, manager, effect); +} + +void addToRenderStateSet(RenderStateSet *stateSet, + const QVector<Qt3DCore::QNodeId> stateIds, + RenderStateManager *manager) +{ + for (const Qt3DCore::QNodeId &stateId : stateIds) { + RenderStateNode *node = manager->lookupResource(stateId); + if (node->isEnabled()) + stateSet->addState(node->impl()); + } +} + +namespace { + +const QString blockArray = QStringLiteral("[%1]"); +const int qNodeIdTypeId = qMetaTypeId<QNodeId>(); + +} + +UniformBlockValueBuilder::UniformBlockValueBuilder() + : updatedPropertiesOnly(false) + , shaderDataManager(nullptr) + , textureManager(nullptr) +{ +} + +UniformBlockValueBuilder::~UniformBlockValueBuilder() +{ +} + +void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData, const QString &blockName, const QString &qmlPropertyName, const QVariant &value) +{ + // In the end, values are either scalar or a scalar array + // Composed elements (structs, structs array) are simplified into simple scalars + if (value.userType() == QMetaType::QVariantList) { // Array + QVariantList list = value.value<QVariantList>(); + if (list.at(0).userType() == qNodeIdTypeId) { // Array of struct qmlPropertyName[i].structMember + for (int i = 0; i < list.size(); ++i) { + const QVariant variantElement = list.at(i); + if (list.at(i).userType() == qNodeIdTypeId) { + const auto nodeId = variantElement.value<QNodeId>(); + ShaderData *subShaderData = shaderDataManager->lookupResource(nodeId); + if (subShaderData) { + buildActiveUniformNameValueMapStructHelper(subShaderData, + blockName + QLatin1Char('.') + qmlPropertyName + blockArray.arg(i), + QLatin1String("")); + } + // Note: we only handle ShaderData as nested container nodes here + } + } + } else { // Array of scalar/vec qmlPropertyName[0] + QString varName = blockName + QLatin1String(".") + qmlPropertyName + QLatin1String("[0]"); + if (uniforms.contains(varName)) { + qCDebug(Shaders) << "UBO array member " << varName << " set for update"; + activeUniformNamesToValue.insert(StringToInt::lookupId(varName), value); + } + } + } else if (value.userType() == qNodeIdTypeId) { // Struct qmlPropertyName.structMember + const auto nodeId = value.value<QNodeId>(); + ShaderData *rSubShaderData = shaderDataManager->lookupResource(nodeId); + if (rSubShaderData) { + buildActiveUniformNameValueMapStructHelper(rSubShaderData, + blockName, + qmlPropertyName); + } else if (textureManager->contains(nodeId)) { + const auto varId = StringToInt::lookupId(blockName + QLatin1Char('.') + qmlPropertyName); + activeUniformNamesToValue.insert(varId, value); + } + } else { // Scalar / Vec + QString varName = blockName + QLatin1Char('.') + qmlPropertyName; + if (uniforms.contains(varName)) { + qCDebug(Shaders) << "UBO scalar member " << varName << " set for update"; + + // If the property needs to be transformed, we transform it here as + // the shaderdata cannot hold transformed properties for multiple + // thread contexts at once + if (currentShaderData->propertyTransformType(qmlPropertyName) != ShaderData::NoTransform) + activeUniformNamesToValue.insert(StringToInt::lookupId(varName), + currentShaderData->getTransformedProperty(qmlPropertyName, viewMatrix)); + else + activeUniformNamesToValue.insert(StringToInt::lookupId(varName), value); + } + } +} + +void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName) +{ + const QHash<QString, QVariant> &properties = rShaderData->properties(); + QHash<QString, QVariant>::const_iterator it = properties.begin(); + const QHash<QString, QVariant>::const_iterator end = properties.end(); + + while (it != end) { + const auto prefix = qmlPropertyName.isEmpty() ? QLatin1String("") : QLatin1String("."); + buildActiveUniformNameValueMapHelper(rShaderData, + blockName + prefix + qmlPropertyName, + it.key(), + it.value()); + ++it; + } +} + +ParameterInfo::ParameterInfo(const int nameId, const HParameter &handle) + : nameId(nameId) + , handle(handle) +{} + +bool ParameterInfo::operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT +{ + return nameId < other.nameId; +} + +bool ParameterInfo::operator<(const int otherNameId) const Q_DECL_NOEXCEPT +{ + return nameId < otherNameId; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/jobs/renderviewjobutils_p.h b/src/render/renderers/opengl/jobs/renderviewjobutils_p.h new file mode 100644 index 000000000..468a95bfd --- /dev/null +++ b/src/render/renderers/opengl/jobs/renderviewjobutils_p.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDERVIEWJOBUTILS_P_H +#define QT3DRENDER_RENDERVIEWJOBUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qt3drender_global.h> +#include <Qt3DCore/qnodeid.h> +#include <QtCore/qhash.h> +#include <QtCore/qvariant.h> +#include <QMatrix4x4> +#include <Qt3DRender/private/uniform_p.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/aligned_malloc_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DCore { +class QFrameAllocator; +} + +namespace Qt3DRender { +namespace Render { + +class FrameGraphNode; +class ParameterManager; +class Effect; +class Entity; +class Material; +class RenderPass; +class RenderStateSet; +class Technique; +class RenderView; +class TechniqueFilter; +class RenderPassFilter; +class Renderer; +class NodeManagers; +class ShaderDataManager; +struct ShaderUniform; +class ShaderData; +class TextureManager; +class RenderStateManager; +class RenderStateCollection; + +Q_AUTOTEST_EXPORT void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, + const FrameGraphNode *fgLeaf); + +Q_AUTOTEST_EXPORT Technique *findTechniqueForEffect(Renderer *renderer, + const TechniqueFilter *techniqueFilter, + Effect *effect); + +typedef QVarLengthArray<RenderPass*, 4> RenderPassList; +Q_AUTOTEST_EXPORT RenderPassList findRenderPassesForTechnique(NodeManagers *manager, + const RenderPassFilter *passFilter, + Technique *technique); + +// Extracts the type T from a QVariant v without using QVariant::value which is slow +// Note: Assumes you are 100% sure about the type you requested +template<typename T> +Q_AUTOTEST_EXPORT inline T variant_value(const QVariant &v) +{ + return *reinterpret_cast<const T *>(v.data()); +} + +struct ParameterInfo +{ + explicit ParameterInfo(const int nameId = -1, const HParameter &handle = HParameter()); + + int nameId; + HParameter handle; + + bool operator<(const int otherNameId) const Q_DECL_NOEXCEPT; + bool operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT; +}; +typedef QVector<ParameterInfo> ParameterInfoList; + +struct RenderPassParameterData +{ + RenderPass *pass; + ParameterInfoList parameterInfo; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, RenderPassParameterData, Q_MOVABLE_TYPE) + +using MaterialParameterGathererData = QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>>; + +Q_AUTOTEST_EXPORT void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList, + ParameterManager *manager, + Material *material, + Effect *effect, + Technique *technique); + +Q_AUTOTEST_EXPORT void addParametersForIds(ParameterInfoList *params, ParameterManager *manager, + const QVector<Qt3DCore::QNodeId> ¶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 addToRenderStateSet(RenderStateSet *stateSet, + const QVector<Qt3DCore::QNodeId> stateIds, + RenderStateManager *manager); + +typedef QHash<int, QVariant> UniformBlockValueBuilderHash; + +struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder +{ + UniformBlockValueBuilder(); + ~UniformBlockValueBuilder(); + + QT3D_ALIGNED_MALLOC_AND_FREE() + + void buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData, + const QString &blockName, + const QString &qmlPropertyName, + const QVariant &value); + void buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, + const QString &blockName, + const QString &qmlPropertyName = QString()); + + bool updatedPropertiesOnly; + QHash<QString, ShaderUniform> uniforms; + UniformBlockValueBuilderHash activeUniformNamesToValue; + ShaderDataManager *shaderDataManager; + TextureManager *textureManager; + Matrix4x4 viewMatrix; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDERVIEWJOBUTILS_P_H diff --git a/src/render/renderers/opengl/opengl.pri b/src/render/renderers/opengl/opengl.pri new file mode 100644 index 000000000..a669bf4ba --- /dev/null +++ b/src/render/renderers/opengl/opengl.pri @@ -0,0 +1,18 @@ + +include (renderer/renderer.pri) +include (jobs/jobs.pri) +include (io/io.pri) +include (textures/textures.pri) +include (graphicshelpers/graphicshelpers.pri) +include (renderstates/renderstates.pri) + +# Qt3D is free of Q_FOREACH - make sure it stays that way: +DEFINES += QT_NO_FOREACH + +gcov { + QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage + QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage +} + +# otherwise mingw headers do not declare common functions like ::strcasecmp +win32-g++*:QMAKE_CXXFLAGS_CXX11 = -std=gnu++0x diff --git a/src/render/renderers/opengl/renderer/commandthread.cpp b/src/render/renderers/opengl/renderer/commandthread.cpp new file mode 100644 index 000000000..387fc1113 --- /dev/null +++ b/src/render/renderers/opengl/renderer/commandthread.cpp @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandthread_p.h" +#include <Qt3DRender/private/glcommands_p.h> +#include <Qt3DRender/private/offscreensurfacehelper_p.h> +#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/shadercache_p.h> +#include <QOpenGLContext> +#include <QOffscreenSurface> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +CommandThread::CommandThread(Renderer *renderer) + : QThread() + , m_renderer(renderer) + , m_waitForStartSemaphore(0) + , m_initializedSemaphore(0) + , m_commandRequestedSemaphore(0) + , m_commandExecutionSemaphore(0) + , m_mainContext(nullptr) + , m_shaderCache(nullptr) + , m_offsreenSurfaceHelper(nullptr) + , m_currentCommand(nullptr) + , m_running(0) +{ +} + +CommandThread::~CommandThread() +{ +} + +void CommandThread::setShaderCache(ShaderCache *shaderCache) +{ + m_shaderCache = shaderCache; +} + +// Called by RenderThread or MainThread (Scene3d) +void CommandThread::initialize(QOpenGLContext *mainContext, OffscreenSurfaceHelper *offsreenSurfaceHelper) +{ + // Start the thread + start(); + + // Wait for thread to be started + m_waitForStartSemaphore.acquire(); + + m_mainContext = mainContext; + m_offsreenSurfaceHelper = offsreenSurfaceHelper; + Q_ASSERT(m_mainContext && offsreenSurfaceHelper); + m_running.fetchAndStoreOrdered(1); + + // Allow thread to proceed + m_initializedSemaphore.release(); +} + +// Called by RenderThread or MainThread (Scene3D) +void CommandThread::shutdown() +{ + m_running.fetchAndStoreOrdered(0); + + // Unblock thread + m_commandRequestedSemaphore.release(1); + + // Wait for thread to exit + wait(); + + // Reset semaphores (in case we ever want to restart) + m_waitForStartSemaphore.acquire(m_waitForStartSemaphore.available()); + m_initializedSemaphore.acquire(m_initializedSemaphore.available()); + m_commandRequestedSemaphore.acquire(m_commandRequestedSemaphore.available()); + m_commandExecutionSemaphore.acquire(m_commandExecutionSemaphore.available()); + m_localContext.reset(); +} + +// Any thread can call this, this is a blocking command +void CommandThread::executeCommand(GLCommand *command) +{ + if (!isRunning()) + return; + + // We lock to prevent any other call to executeCommand to be executed + // before we have received the result of our command + m_blockingCallerMutex.lock(); + + // Store command to be executed + m_currentCommand = command; + + // Allow thread to proceed and execute command + m_commandRequestedSemaphore.release(); + + // Wait for thread to be done + m_commandExecutionSemaphore.acquire(); + + // Reset command + m_currentCommand = nullptr; + + // Unlock blocking semaphore so that other calls to executeCommand + // can proceed + m_blockingCallerMutex.unlock(); +} + +void CommandThread::run() +{ + // Allow initialize to proceed + m_waitForStartSemaphore.release(); + + // Wait for initialize to be completed + m_initializedSemaphore.acquire(); + + Q_ASSERT(m_mainContext && m_shaderCache); + + // Initialize shared context and resources for the thread + m_localContext.reset(new QOpenGLContext()); + m_localContext->setShareContext(m_mainContext); + m_localContext->create(); + + // Initialize GraphicsContext + m_graphicsContext.reset(new GraphicsContext()); + m_graphicsContext->setShaderCache(m_shaderCache); + m_graphicsContext->setOpenGLContext(m_localContext.data()); + + bool initialized = false; + while (true) { + + // Wait for command + m_commandRequestedSemaphore.acquire(); + + // Are we still running? + if (!m_running.load()) { + m_graphicsContext->doneCurrent(); + // to prevent executeCommand being locked + m_commandExecutionSemaphore.release(); + break; + } + + if (Q_UNLIKELY(!initialized)) { + QOffscreenSurface *offscreenSurface = m_offsreenSurfaceHelper->offscreenSurface(); + Q_ASSERT(offscreenSurface); + m_graphicsContext->makeCurrent(offscreenSurface); + initialized = true; + } + + m_currentCommand->execute(m_renderer, m_graphicsContext.data()); + + // Allow caller to proceed as we are done with the command + m_commandExecutionSemaphore.release(); + } +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/commandthread_p.h b/src/render/renderers/opengl/renderer/commandthread_p.h new file mode 100644 index 000000000..0508675c4 --- /dev/null +++ b/src/render/renderers/opengl/renderer/commandthread_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_COMMANDTHREAD_P_H +#define QT3DRENDER_RENDER_COMMANDTHREAD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QThread> +#include <QtCore/QSemaphore> +#include <QtCore/QMutex> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; + +namespace Qt3DRender { + +namespace Render { + +class Renderer; +class GLCommand; +class OffscreenSurfaceHelper; +class GraphicsContext; +class ShaderCache; + +class CommandThread : public QThread +{ + Q_OBJECT +public: + explicit CommandThread(Renderer *renderer); + ~CommandThread(); + + Render::Renderer* renderer() const { return m_renderer; } + + void setShaderCache(ShaderCache *shaderCache); + ShaderCache *shaderCache() const { return m_shaderCache; } + + void initialize(QOpenGLContext *mainContext, OffscreenSurfaceHelper *offsreenSurfaceHelper); + void shutdown(); + + void executeCommand(GLCommand *command); + +private: + void run() override; + void executeCommandInternal(Qt3DRender::Render::GLCommand *command); + +private: + Renderer* m_renderer; + QSemaphore m_waitForStartSemaphore; + QSemaphore m_initializedSemaphore; + QSemaphore m_commandRequestedSemaphore; + QSemaphore m_commandExecutionSemaphore; + QMutex m_blockingCallerMutex; + QOpenGLContext *m_mainContext; + ShaderCache *m_shaderCache; + OffscreenSurfaceHelper *m_offsreenSurfaceHelper; + QScopedPointer<QOpenGLContext> m_localContext; + QScopedPointer<GraphicsContext> m_graphicsContext; + GLCommand *m_currentCommand; + QAtomicInt m_running; +}; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_COMMANDTHREAD_P_H diff --git a/src/render/renderers/opengl/renderer/glcommands.cpp b/src/render/renderers/opengl/renderer/glcommands.cpp new file mode 100644 index 000000000..fd7ee9fe8 --- /dev/null +++ b/src/render/renderers/opengl/renderer/glcommands.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "glcommands_p.h" +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +LoadShaderCommand::LoadShaderCommand(Shader *shader) + : m_shader(shader) +{ + Q_ASSERT(m_shader); +} + +void LoadShaderCommand::execute(Renderer *renderer, GraphicsContext *ctx) +{ + NodeManagers *nodeManagers = renderer->nodeManagers(); + ctx->loadShader(m_shader, nodeManagers->shaderManager()); +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/glcommands_p.h b/src/render/renderers/opengl/renderer/glcommands_p.h new file mode 100644 index 000000000..5ed360759 --- /dev/null +++ b/src/render/renderers/opengl/renderer/glcommands_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GLCOMMANDS_P_H +#define QT3DRENDER_RENDER_GLCOMMANDS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qt3drender_global.h> + +QT_BEGIN_NAMESPACE + + +namespace Qt3DRender { + +namespace Render { + +class GraphicsContext; +class Renderer; +class Shader; + +class GLCommand +{ +public: + virtual void execute(Renderer *renderer, GraphicsContext *ctx) = 0; +}; + +class Q_AUTOTEST_EXPORT LoadShaderCommand : public GLCommand +{ +public: + explicit LoadShaderCommand(Shader *shader); + Shader *shader() const { return m_shader; } + void execute(Renderer *renderer, GraphicsContext *ctx) Q_DECL_OVERRIDE; + +private: + Shader *m_shader = nullptr; +}; +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLCOMMANDS_P_H diff --git a/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp b/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp new file mode 100644 index 000000000..0c4fd8c9d --- /dev/null +++ b/src/render/renderers/opengl/renderer/openglvertexarrayobject.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "openglvertexarrayobject_p.h" +#include <Qt3DRender/private/submissioncontext_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/managers_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +OpenGLVertexArrayObject::OpenGLVertexArrayObject() + : m_ctx(nullptr) + , m_specified(false) + , m_supportsVao(false) +{} + +void OpenGLVertexArrayObject::bind() +{ + Q_ASSERT(m_ctx); + if (m_supportsVao) { + Q_ASSERT(!m_vao.isNull()); + Q_ASSERT(m_vao->isCreated()); + m_vao->bind(); + } else { + // Unbind any other VAO that may have been bound and not released correctly + if (m_ctx->m_currentVAO != nullptr && m_ctx->m_currentVAO != this) + m_ctx->m_currentVAO->release(); + + m_ctx->m_currentVAO = this; + // We need to specify array and vertex attributes + for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) + m_ctx->enableAttribute(attr); + if (!m_indexAttribute.isNull()) + m_ctx->bindGLBuffer(m_ctx->m_renderer->nodeManagers()->glBufferManager()->data(m_indexAttribute), + GLBuffer::IndexBuffer); + } +} + +void OpenGLVertexArrayObject::release() +{ + Q_ASSERT(m_ctx); + if (m_supportsVao) { + Q_ASSERT(!m_vao.isNull()); + Q_ASSERT(m_vao->isCreated()); + m_vao->release(); + } else { + if (m_ctx->m_currentVAO == this) { + for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) + m_ctx->disableAttribute(attr); + m_ctx->m_currentVAO = nullptr; + } + } +} + +// called from Render thread +void OpenGLVertexArrayObject::create(SubmissionContext *ctx, const VAOIdentifier &key) +{ + QMutexLocker lock(&m_mutex); + + Q_ASSERT(!m_ctx && !m_vao); + + m_ctx = ctx; + m_supportsVao = m_ctx->supportsVAO(); + if (m_supportsVao) { + m_vao.reset(new QOpenGLVertexArrayObject()); + m_vao->create(); + } + m_owners = key; +} + +// called from Render thread +void OpenGLVertexArrayObject::destroy() +{ + QMutexLocker locker(&m_mutex); + + Q_ASSERT(m_ctx); + cleanup(); +} + +void OpenGLVertexArrayObject::cleanup() +{ + m_vao.reset(); + m_ctx = nullptr; + m_specified = false; + m_supportsVao = false; + m_indexAttribute = SubmissionContext::VAOIndexAttribute(); + m_vertexAttributes.clear(); +} + +// called from job +bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr) +{ + QMutexLocker lock(&m_mutex); + + if (!m_ctx) + return false; + + const bool geometryExists = (geomMgr->data(m_owners.first) != nullptr); + const bool shaderExists = (shaderMgr->data(m_owners.second) != nullptr); + + return !geometryExists || !shaderExists; +} + +void OpenGLVertexArrayObject::saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr) +{ + // Remove any vertexAttribute already at location + for (auto i = m_vertexAttributes.size() - 1; i >= 0; --i) { + if (m_vertexAttributes.at(i).location == attr.location) { + m_vertexAttributes.removeAt(i); + break; + } + } + m_vertexAttributes.push_back(attr); +} + + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h b/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h new file mode 100644 index 000000000..eee837221 --- /dev/null +++ b/src/render/renderers/opengl/renderer/openglvertexarrayobject_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPENGLVERTEXARRAYOBJECT_H +#define OPENGLVERTEXARRAYOBJECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qopenglvertexarrayobject.h> +#include <Qt3DRender/private/submissioncontext_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class GeometryManager; +class ShaderManager; + +typedef QPair<HGeometry, HShader> VAOIdentifier; + +class OpenGLVertexArrayObject +{ +public: + OpenGLVertexArrayObject(); + + void bind(); + void release(); + + void create(SubmissionContext *ctx, const VAOIdentifier &key); + void destroy(); + void cleanup(); + + bool isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr); + + QOpenGLVertexArrayObject *vao() { return m_vao.data(); } + const QOpenGLVertexArrayObject *vao() const { return m_vao.data(); } + + void setSpecified(bool b) { m_specified = b; } + bool isSpecified() const { return m_specified; } + + +private: + QMutex m_mutex; + SubmissionContext *m_ctx; + QScopedPointer<QOpenGLVertexArrayObject> m_vao; + bool m_specified; + bool m_supportsVao; + VAOIdentifier m_owners; + + friend class SubmissionContext; + + void saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr); + inline void saveIndexAttribute(HGLBuffer glBufferHandle) { m_indexAttribute = glBufferHandle; } + + QVector<SubmissionContext::VAOVertexAttribute> m_vertexAttributes; + SubmissionContext::VAOIndexAttribute m_indexAttribute; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // OPENGLVERTEXARRAYOBJECT_H diff --git a/src/render/renderers/opengl/renderer/rendercommand.cpp b/src/render/renderers/opengl/renderer/rendercommand.cpp new file mode 100644 index 000000000..e60b17668 --- /dev/null +++ b/src/render/renderers/opengl/renderer/rendercommand.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rendercommand_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +RenderCommand::RenderCommand() + : m_stateSet(nullptr) + , m_depth(0.0f) + , m_changeCost(0) + , m_type(RenderCommand::Draw) + , m_primitiveCount(0) + , m_primitiveType(QGeometryRenderer::Triangles) + , m_restartIndexValue(-1) + , m_firstInstance(0) + , m_firstVertex(0) + , m_verticesPerPatch(0) + , m_instanceCount(0) + , m_indexOffset(0) + , m_indexAttributeByteOffset(0) + , m_indexAttributeDataType(GL_UNSIGNED_SHORT) + , m_indirectAttributeByteOffset(0) + , m_drawIndexed(false) + , m_drawIndirect(false) + , m_primitiveRestartEnabled(false) + , m_isValid(false) +{ + m_workGroups[0] = 0; + m_workGroups[1] = 0; + m_workGroups[2] = 0; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/rendercommand_p.h b/src/render/renderers/opengl/renderer/rendercommand_p.h new file mode 100644 index 000000000..67e02d35b --- /dev/null +++ b/src/render/renderers/opengl/renderer/rendercommand_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERCOMMAND_H +#define QT3DRENDER_RENDER_RENDERCOMMAND_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qglobal.h> +#include <Qt3DRender/private/shaderparameterpack_p.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/qgeometryrenderer.h> +#include <QOpenGLShaderProgram> +#include <QOpenGLTexture> +#include <QMatrix4x4> + +QT_BEGIN_NAMESPACE + +class QOpenGLVertexArrayObject; + +namespace Qt3DRender { + +namespace Render { + +class RenderStateSet; + +class Q_AUTOTEST_EXPORT RenderCommand +{ +public: + RenderCommand(); + + HVao m_vao; // VAO used during the submission step to store all states and VBOs + HShader m_shader; // Shader for given pass and mesh + HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....) + ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy the + // Texture while submission is happening. + RenderStateSet *m_stateSet; + + HGeometry m_geometry; + HGeometryRenderer m_geometryRenderer; + + HBuffer m_indirectDrawBuffer; // Reference to indirect draw buffer (valid only m_drawIndirect == true) + + // A QAttribute pack might be interesting + // This is a temporary fix in the meantime, to remove the hacked methods in Technique + QVector<int> m_attributes; + + float m_depth; + int m_changeCost; + uint m_shaderDna; + + enum CommandType { + Draw, + Compute + }; + + CommandType m_type; + int m_workGroups[3]; + + // Values filled for draw calls + GLsizei m_primitiveCount; + QGeometryRenderer::PrimitiveType m_primitiveType; + int m_restartIndexValue; + int m_firstInstance; + int m_firstVertex; + int m_verticesPerPatch; + int m_instanceCount; + int m_indexOffset; + uint m_indexAttributeByteOffset; + GLint m_indexAttributeDataType; + uint m_indirectAttributeByteOffset; + bool m_drawIndexed; + bool m_drawIndirect; + bool m_primitiveRestartEnabled; + bool m_isValid; +}; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERCOMMAND_H diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp new file mode 100644 index 000000000..2611fb6cc --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -0,0 +1,2067 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderer_p.h" + +#include <Qt3DCore/qentity.h> + +#include <Qt3DRender/qmaterial.h> +#include <Qt3DRender/qmesh.h> +#include <Qt3DRender/qrenderpass.h> +#include <Qt3DRender/qshaderprogram.h> +#include <Qt3DRender/qtechnique.h> +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DRender/qeffect.h> + +#include <Qt3DRender/private/qsceneimporter_p.h> +#include <Qt3DRender/private/renderstates_p.h> +#include <Qt3DRender/private/cameraselectornode_p.h> +#include <Qt3DRender/private/framegraphvisitor_p.h> +#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/cameralens_p.h> +#include <Qt3DRender/private/rendercommand_p.h> +#include <Qt3DRender/private/entity_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/material_p.h> +#include <Qt3DRender/private/renderpassfilternode_p.h> +#include <Qt3DRender/private/renderqueue_p.h> +#include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/glbuffer_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/technique_p.h> +#include <Qt3DRender/private/renderthread_p.h> +#include <Qt3DRender/private/renderview_p.h> +#include <Qt3DRender/private/scenemanager_p.h> +#include <Qt3DRender/private/techniquefilternode_p.h> +#include <Qt3DRender/private/viewportnode_p.h> +#include <Qt3DRender/private/vsyncframeadvanceservice_p.h> +#include <Qt3DRender/private/pickeventfilter_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/gltexturemanager_p.h> +#include <Qt3DRender/private/gltexture_p.h> +#include <Qt3DRender/private/geometryrenderermanager_p.h> +#include <Qt3DRender/private/techniquemanager_p.h> +#include <Qt3DRender/private/openglvertexarrayobject_p.h> +#include <Qt3DRender/private/platformsurfacefilter_p.h> +#include <Qt3DRender/private/loadbufferjob_p.h> +#include <Qt3DRender/private/rendercapture_p.h> +#include <Qt3DRender/private/updatelevelofdetailjob_p.h> +#include <Qt3DRender/private/buffercapture_p.h> +#include <Qt3DRender/private/offscreensurfacehelper_p.h> +#include <Qt3DRender/private/renderviewbuilder_p.h> +#include <Qt3DRender/private/commandthread_p.h> +#include <Qt3DRender/private/glcommands_p.h> + +#include <Qt3DRender/qcameralens.h> +#include <Qt3DCore/private/qeventfilterservice_p.h> +#include <Qt3DCore/private/qabstractaspectjobmanager_p.h> +#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h> + +#if QT_CONFIG(qt3d_profile_jobs) +#include <Qt3DCore/private/aspectcommanddebugger_p.h> +#endif + +#include <QStack> +#include <QOffscreenSurface> +#include <QSurface> +#include <QElapsedTimer> +#include <QLibraryInfo> +#include <QPluginLoader> +#include <QDir> +#include <QUrl> +#include <QOffscreenSurface> +#include <QWindow> + +#include <QtGui/private/qopenglcontext_p.h> + +// For Debug purposes only +#include <QThread> + + +#if QT_CONFIG(qt3d_profile_jobs) +#include <Qt3DCore/private/qthreadpooler_p.h> +#include <Qt3DRender/private/job_common_p.h> +#include <Qt3DRender/private/commandexecuter_p.h> +#endif + +#include <Qt3DRender/private/frameprofiler_p.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt3DCore; + +namespace Qt3DRender { +namespace Render { +/*! + \internal + + Renderer shutdown procedure: + + Since the renderer relies on the surface and OpenGLContext to perform its cleanup, + it is shutdown when the surface is set to nullptr + + When the surface is set to nullptr this will request the RenderThread to terminate + and will prevent createRenderBinJobs from returning a set of jobs as there is nothing + more to be rendered. + + In turn, this will call shutdown which will make the OpenGL context current one last time + to allow cleanups requiring a call to QOpenGLContext::currentContext to execute properly. + At the end of that function, the GraphicsContext is set to null. + + At this point though, the QAspectThread is still running its event loop and will only stop + a short while after. + */ + +Renderer::Renderer(QRenderAspect::RenderType type) + : m_services(nullptr) + , m_nodesManager(nullptr) + , m_renderSceneRoot(nullptr) + , m_defaultRenderStateSet(nullptr) + , m_submissionContext(nullptr) + , m_renderQueue(new RenderQueue()) + , m_renderThread(type == QRenderAspect::Threaded ? new RenderThread(this) : nullptr) + , m_commandThread(new CommandThread(this)) + , m_vsyncFrameAdvanceService(new VSyncFrameAdvanceService(m_renderThread != nullptr)) + , m_waitForInitializationToBeCompleted(0) + , m_pickEventFilter(new PickEventFilter()) + , m_exposed(0) + , m_lastFrameCorrect(0) + , m_glContext(nullptr) + , m_shareContext(nullptr) + , m_shaderCache(new ShaderCache()) + , m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create()) + , m_rayCastingJob(RayCastingJobPtr::create()) + , m_time(0) + , m_settings(nullptr) + , m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create()) + , m_cleanupJob(Render::FrameCleanupJobPtr::create()) + , m_worldTransformJob(Render::UpdateWorldTransformJobPtr::create()) + , m_expandBoundingVolumeJob(Render::ExpandBoundingVolumeJobPtr::create()) + , m_calculateBoundingVolumeJob(Render::CalculateBoundingVolumeJobPtr::create()) + , m_updateWorldBoundingVolumeJob(Render::UpdateWorldBoundingVolumeJobPtr::create()) + , m_updateTreeEnabledJob(Render::UpdateTreeEnabledJobPtr::create()) + , m_sendRenderCaptureJob(Render::SendRenderCaptureJobPtr::create(this)) + , m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create()) + , m_updateSkinningPaletteJob(Render::UpdateSkinningPaletteJobPtr::create()) + , m_updateLevelOfDetailJob(Render::UpdateLevelOfDetailJobPtr::create()) + , m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create()) + , m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create()) + , m_bufferGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering)) + , m_vaoGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering)) + , m_textureGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering)) + , m_introspectShaderJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { reloadDirtyShaders(); }, JobTypes::DirtyShaderGathering)) + , m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading)) + , m_ownedContext(false) + , m_offscreenHelper(nullptr) + #if QT_CONFIG(qt3d_profile_jobs) + , m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)) + #endif +{ + // Set renderer as running - it will wait in the context of the + // RenderThread for RenderViews to be submitted + m_running.fetchAndStoreOrdered(1); + if (m_renderThread) + m_renderThread->waitForStart(); + + // Create jobs to update transforms and bounding volumes + // We can only update bounding volumes once all world transforms are known + m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob); + m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob); + m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob); + m_updateShaderDataTransformJob->addDependency(m_worldTransformJob); + m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob); + // m_calculateBoundingVolumeJob's dependency on m_updateTreeEnabledJob is set in renderBinJobs + + // Dirty texture gathering depends on m_syncTextureLoadingJob + // m_syncTextureLoadingJob will depend on the texture loading jobs + m_textureGathererJob->addDependency(m_syncTextureLoadingJob); + + // Ensures all skeletons are loaded before we try to update them + m_updateSkinningPaletteJob->addDependency(m_syncTextureLoadingJob); + + // All world stuff depends on the RenderEntity's localBoundingVolume + m_updateLevelOfDetailJob->addDependency(m_updateMeshTriangleListJob); + m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob); + m_rayCastingJob->addDependency(m_updateMeshTriangleListJob); + + m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob); + + m_filterCompatibleTechniqueJob->setRenderer(this); + + m_defaultRenderStateSet = new RenderStateSet; + m_defaultRenderStateSet->addState(RenderStateSet::createState<DepthTest>(GL_LESS)); + m_defaultRenderStateSet->addState(RenderStateSet::createState<CullFace>(GL_BACK)); + m_defaultRenderStateSet->addState(RenderStateSet::createState<ColorMask>(true, true, true, true)); +} + +Renderer::~Renderer() +{ + // If using a threaded rendering approach, tell the thread to exit + // and wait for it to be done + m_running.fetchAndStoreOrdered(0); + if (m_renderThread) + m_renderThread->wait(); + + delete m_renderQueue; + delete m_defaultRenderStateSet; + delete m_shaderCache; + + if (!m_ownedContext) + QObject::disconnect(m_contextConnection); +} + +void Renderer::dumpInfo() const +{ + qDebug() << Q_FUNC_INFO << "t =" << m_time; + + const ShaderManager *shaderManager = m_nodesManager->shaderManager(); + qDebug() << "=== Shader Manager ==="; + qDebug() << *shaderManager; + + const TextureManager *textureManager = m_nodesManager->textureManager(); + qDebug() << "=== Texture Manager ==="; + qDebug() << *textureManager; + + const TextureImageManager *textureImageManager = m_nodesManager->textureImageManager(); + qDebug() << "=== Texture Image Manager ==="; + qDebug() << *textureImageManager; +} + +qint64 Renderer::time() const +{ + return m_time; +} + +void Renderer::setTime(qint64 time) +{ + m_time = time; +} + +void Renderer::setNodeManagers(NodeManagers *managers) +{ + m_nodesManager = managers; + + m_updateShaderDataTransformJob->setManagers(m_nodesManager); + m_cleanupJob->setManagers(m_nodesManager); + m_calculateBoundingVolumeJob->setManagers(m_nodesManager); + m_pickBoundingVolumeJob->setManagers(m_nodesManager); + m_rayCastingJob->setManagers(m_nodesManager); + m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager()); + m_sendRenderCaptureJob->setManagers(m_nodesManager); + m_sendBufferCaptureJob->setManagers(m_nodesManager); + m_updateLevelOfDetailJob->setManagers(m_nodesManager); + m_updateSkinningPaletteJob->setManagers(m_nodesManager); + m_updateMeshTriangleListJob->setManagers(m_nodesManager); + m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager()); +} + +void Renderer::setServices(QServiceLocator *services) +{ + m_services = services; + + m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService()); +} + +NodeManagers *Renderer::nodeManagers() const +{ + return m_nodesManager; +} + +/*! + \internal + + Return context which can be used to share resources safely + with qt3d main render context. +*/ +QOpenGLContext *Renderer::shareContext() const +{ + QMutexLocker lock(&m_shareContextMutex); + return m_shareContext ? m_shareContext + : (m_submissionContext->openGLContext() + ? m_submissionContext->openGLContext()->shareContext() + : nullptr); +} + +// Executed in the command thread +void Renderer::loadShader(Shader *shader) const +{ + Profiling::GLTimeRecorder recorder(Profiling::ShaderUpload); + LoadShaderCommand cmd(shader); + m_commandThread->executeCommand(&cmd); +} + +void Renderer::setOpenGLContext(QOpenGLContext *context) +{ + m_glContext = context; +} + +// Called in RenderThread context by the run method of RenderThread +// RenderThread has locked the mutex already and unlocks it when this +// method termintates +void Renderer::initialize() +{ + m_submissionContext.reset(new SubmissionContext); + m_submissionContext->setRenderer(this); + + QOpenGLContext* ctx = m_glContext; + + { + QMutexLocker lock(&m_shareContextMutex); + // If we are using our own context (not provided by QtQuick), + // we need to create it + if (!m_glContext) { + ctx = new QOpenGLContext; + ctx->setShareContext(qt_gl_global_share_context()); + + // TO DO: Shouldn't we use the highest context available and trust + // QOpenGLContext to fall back on the best lowest supported ? + const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING"); + + if (!debugLoggingMode.isEmpty()) { + QSurfaceFormat sf = ctx->format(); + sf.setOption(QSurfaceFormat::DebugContext); + ctx->setFormat(sf); + } + + // Create OpenGL context + if (ctx->create()) + qCDebug(Backend) << "OpenGL context created with actual format" << ctx->format(); + else + qCWarning(Backend) << Q_FUNC_INFO << "OpenGL context creation failed"; + m_ownedContext = true; + } else { + // Context is not owned by us, so we need to know if it gets destroyed + m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed, + [this] { releaseGraphicsResources(); }); + } + + if (!ctx->shareContext()) { + m_shareContext = new QOpenGLContext; + m_shareContext->setFormat(ctx->format()); + m_shareContext->setShareContext(ctx); + m_shareContext->create(); + } + + // Set shader cache on submission context and command thread + m_submissionContext->setShaderCache(m_shaderCache); + m_commandThread->setShaderCache(m_shaderCache); + + // Note: we don't have a surface at this point + // The context will be made current later on (at render time) + m_submissionContext->setOpenGLContext(ctx); + + // Store the format used by the context and queue up creating an + // offscreen surface in the main thread so that it is available + // for use when we want to shutdown the renderer. We need to create + // the offscreen surface on the main thread because on some platforms + // (MS Windows), an offscreen surface is just a hidden QWindow. + m_format = ctx->format(); + QMetaObject::invokeMethod(m_offscreenHelper, "createOffscreenSurface"); + + // Initialize command thread (uses the offscreen surface to make its own ctx current) + m_commandThread->initialize(ctx, m_offscreenHelper); + // Note: the offscreen surface is also used at shutdown time to release resources + // of the submission gl context (when the window is already gone). + // By that time (in releaseGraphicResources), the commandThread has been destroyed + // and the offscreenSurface can be reused + } + + // Awake setScenegraphRoot in case it was waiting + m_waitForInitializationToBeCompleted.release(1); + // Allow the aspect manager to proceed + m_vsyncFrameAdvanceService->proceedToNextFrame(); +} + +/*! + * \internal + * + * Signals for the renderer to stop rendering. If a threaded renderer is in use, + * the render thread will call releaseGraphicsResources() just before the thread exits. + * If rendering synchronously, this function will call releaseGraphicsResources(). + */ +void Renderer::shutdown() +{ + qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer shutdown"; + m_running.store(0); + + // We delete any renderqueue that we may not have had time to render + // before the surface was destroyed + qDeleteAll(m_renderQueue->nextFrameQueue()); + m_renderQueue->reset(); + + m_commandThread->shutdown(); + + if (!m_renderThread) { + releaseGraphicsResources(); + } else { + // Wake up the render thread in case it is waiting for some renderviews + // to be ready. The isReadyToSubmit() function checks for a shutdown + // having been requested. + m_submitRenderViewsSemaphore.release(1); + m_renderThread->wait(); + } +} + +/*! + \internal + + When using a threaded renderer this function is called in the context of the + RenderThread to do any shutdown and cleanup that needs to be performed in the + thread where the OpenGL context lives. + + When using Scene3D or anything that provides a custom QOpenGLContext (not + owned by Qt3D) this function is called whenever the signal + QOpenGLContext::aboutToBeDestroyed is emitted. In that case this function + is called in the context of the emitter's thread. +*/ +void Renderer::releaseGraphicsResources() +{ + // We may get called twice when running inside of a Scene3D. Once when Qt Quick + // wants to shutdown, and again when the render aspect gets unregistered. So + // check that we haven't already cleaned up before going any further. + if (!m_submissionContext) + return; + + // Try to temporarily make the context current so we can free up any resources + QMutexLocker locker(&m_offscreenSurfaceMutex); + QOffscreenSurface *offscreenSurface = m_offscreenHelper->offscreenSurface(); + if (!offscreenSurface) { + qWarning() << "Failed to make context current: OpenGL resources will not be destroyed"; + return; + } + + QOpenGLContext *context = m_submissionContext->openGLContext(); + Q_ASSERT(context); + if (context->makeCurrent(offscreenSurface)) { + + // Clean up the graphics context and any resources + const QVector<GLTexture*> activeTextures = m_nodesManager->glTextureManager()->activeResources(); + for (GLTexture *tex : activeTextures) + tex->destroyGLTexture(); + + // Do the same thing with buffers + const QVector<HGLBuffer> activeBuffers = m_nodesManager->glBufferManager()->activeHandles(); + for (const HGLBuffer &bufferHandle : activeBuffers) { + GLBuffer *buffer = m_nodesManager->glBufferManager()->data(bufferHandle); + buffer->destroy(m_submissionContext.data()); + } + + // Do the same thing with VAOs + const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles(); + for (const HVao &vaoHandle : activeVaos) { + OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle); + vao->destroy(); + } + + context->doneCurrent(); + } else { + qWarning() << "Failed to make context current: OpenGL resources will not be destroyed"; + } + + if (m_ownedContext) + delete context; + if (m_shareContext) + delete m_shareContext; + + m_submissionContext.reset(nullptr); + qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown"; +} + +void Renderer::setSurfaceExposed(bool exposed) +{ + qCDebug(Backend) << "Window exposed: " << exposed; + m_exposed.fetchAndStoreOrdered(exposed); +} + +Render::FrameGraphNode *Renderer::frameGraphRoot() const +{ + Q_ASSERT(m_settings); + if (m_nodesManager && m_nodesManager->frameGraphManager() && m_settings) + return m_nodesManager->frameGraphManager()->lookupNode(m_settings->activeFrameGraphID()); + return nullptr; +} + +// QAspectThread context +// Order of execution : +// 1) RenderThread is created -> release 1 of m_waitForInitializationToBeCompleted when started +// 2) setSceneRoot waits to acquire initialization +// 3) submitRenderView -> check for surface +// -> make surface current + create proper glHelper if needed +void Renderer::setSceneRoot(QBackendNodeFactory *factory, Entity *sgRoot) +{ + Q_ASSERT(sgRoot); + Q_UNUSED(factory); + + // If initialization hasn't been completed we must wait + m_waitForInitializationToBeCompleted.acquire(); + + m_renderSceneRoot = sgRoot; + if (!m_renderSceneRoot) + qCWarning(Backend) << "Failed to build render scene"; + m_renderSceneRoot->dump(); + qCDebug(Backend) << Q_FUNC_INFO << "DUMPING SCENE"; + + // Set the scene root on the jobs + m_worldTransformJob->setRoot(m_renderSceneRoot); + m_expandBoundingVolumeJob->setRoot(m_renderSceneRoot); + m_calculateBoundingVolumeJob->setRoot(m_renderSceneRoot); + m_cleanupJob->setRoot(m_renderSceneRoot); + m_pickBoundingVolumeJob->setRoot(m_renderSceneRoot); + m_rayCastingJob->setRoot(m_renderSceneRoot); + m_updateLevelOfDetailJob->setRoot(m_renderSceneRoot); + m_updateSkinningPaletteJob->setRoot(m_renderSceneRoot); + m_updateTreeEnabledJob->setRoot(m_renderSceneRoot); + + // Set all flags to dirty + m_dirtyBits.marked |= AbstractRenderer::AllDirty; +} + +void Renderer::registerEventFilter(QEventFilterService *service) +{ + qCDebug(Backend) << Q_FUNC_INFO << QThread::currentThread(); + service->registerEventFilter(m_pickEventFilter.data(), 1024); +} + +void Renderer::setSettings(RenderSettings *settings) +{ + m_settings = settings; +} + +RenderSettings *Renderer::settings() const +{ + return m_settings; +} + +void Renderer::render() +{ + // Traversing the framegraph tree from root to lead node + // Allows us to define the rendering set up + // Camera, RenderTarget ... + + // Utimately the renderer should be a framework + // For the processing of the list of renderviews + + // Matrice update, bounding volumes computation ... + // Should be jobs + + // namespace Qt3DCore has 2 distincts node trees + // One scene description + // One framegraph description + + while (m_running.load() > 0) { + doRender(); + // TO DO: Restore windows exposed detection + // Probably needs to happens some place else though + } +} + +void Renderer::doRender(bool scene3dBlocking) +{ + Renderer::ViewSubmissionResultData submissionData; + bool hasCleanedQueueAndProceeded = false; + bool preprocessingComplete = false; + bool beganDrawing = false; + const bool canSubmit = isReadyToSubmit(); + + // Lock the mutex to protect access to the renderQueue while we look for its state + QMutexLocker locker(m_renderQueue->mutex()); + bool queueIsComplete = m_renderQueue->isFrameQueueComplete(); + const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0; + + // Scene3D Blocking Mode + if (scene3dBlocking && !queueIsComplete && !queueIsEmpty) { + int i = 0; + // We wait at most 10ms to avoid a case we could never recover from + while (!queueIsComplete && i++ < 10) { + QThread::msleep(1); + qCDebug(Backend) << Q_FUNC_INFO << "Waiting for ready queue (try:" << i << "/ 10)"; + locker.unlock(); + queueIsComplete = m_renderQueue->isFrameQueueComplete(); + locker.relock(); + } + } + + // When using synchronous rendering (QtQuick) + // We are not sure that the frame queue is actually complete + // Since a call to render may not be synched with the completions + // of the RenderViewJobs + // In such a case we return early, waiting for a next call with + // the frame queue complete at this point + + // RenderQueue is complete (but that means it may be of size 0) + if (canSubmit && (queueIsComplete && !queueIsEmpty)) { + const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue(); + +#if QT_CONFIG(qt3d_profile_jobs) + // Save start of frame + JobRunStats submissionStatsPart1; + JobRunStats submissionStatsPart2; + submissionStatsPart1.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart1; + submissionStatsPart1.jobId.typeAndInstance[1] = 0; + submissionStatsPart1.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); + submissionStatsPart1.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); + submissionStatsPart2.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart2; + submissionStatsPart2.jobId.typeAndInstance[1] = 0; + submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); +#endif + + if (canRender()) { + { // Scoped to destroy surfaceLock + QSurface *surface = nullptr; + for (const Render::RenderView *rv: renderViews) { + surface = rv->surface(); + if (surface) + break; + } + + SurfaceLocker surfaceLock(surface); + const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid()); + if (surfaceIsValid) { + // Reset state for each draw if we don't have complete control of the context + if (!m_ownedContext) + m_submissionContext->setCurrentStateSet(nullptr); + beganDrawing = m_submissionContext->beginDrawing(surface); + if (beganDrawing) { + // 1) Execute commands for buffer uploads, texture updates, shader loading first + updateGLResources(); + // 2) Update VAO and copy data into commands to allow concurrent submission + prepareCommandsSubmission(renderViews); + preprocessingComplete = true; + } + } + } + // 2) Proceed to next frame and start preparing frame n + 1 + m_renderQueue->reset(); + locker.unlock(); // Done protecting RenderQueue + m_vsyncFrameAdvanceService->proceedToNextFrame(); + hasCleanedQueueAndProceeded = true; + +#if QT_CONFIG(qt3d_profile_jobs) + if (preprocessingComplete) { + submissionStatsPart2.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); + submissionStatsPart1.endTime = submissionStatsPart2.startTime; + } +#endif + // Only try to submit the RenderViews if the preprocessing was successful + // This part of the submission is happening in parallel to the RV building for the next frame + if (preprocessingComplete) { + // 3) Submit the render commands for frame n (making sure we never reference something that could be changing) + // Render using current device state and renderer configuration + submissionData = submitRenderViews(renderViews); + + // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader deleted...) + cleanGraphicsResources(); + } + } + +#if QT_CONFIG(qt3d_profile_jobs) + // Execute the pending shell commands + m_commandExecuter->performAsynchronousCommandExecution(renderViews); +#endif + + // Delete all the RenderViews which will clear the allocators + // that were used for their allocation + qDeleteAll(renderViews); + +#if QT_CONFIG(qt3d_profile_jobs) + if (preprocessingComplete) { + // Save submission elapsed time + submissionStatsPart2.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); + // Note this is safe since proceedToNextFrame is the one going to trigger + // the write to the file, and this is performed after this step + Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart1); + Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart2); + Profiling::GLTimeRecorder::writeResults(); + } +#endif + } + + // Only reset renderQueue and proceed to next frame if the submission + // succeeded or if we are using a render thread and that is wasn't performed + // already + + // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong + // with the rendering and/or the renderqueue is incomplete from some reason + // (in the case of scene3d the render jobs may be taking too long ....) + // or alternatively it could be complete but empty (RenderQueue of size 0) + if (!hasCleanedQueueAndProceeded && + (m_renderThread || queueIsComplete || queueIsEmpty)) { + // RenderQueue was full but something bad happened when + // trying to render it and therefore proceedToNextFrame was not called + // Note: in this case the renderQueue mutex is still locked + + // Reset the m_renderQueue so that we won't try to render + // with a queue used by a previous frame with corrupted content + // if the current queue was correctly submitted + m_renderQueue->reset(); + + // We allow the RenderTickClock service to proceed to the next frame + // In turn this will allow the aspect manager to request a new set of jobs + // to be performed for each aspect + m_vsyncFrameAdvanceService->proceedToNextFrame(); + } + + // Perform the last swapBuffers calls after the proceedToNextFrame + // as this allows us to gain a bit of time for the preparation of the + // next frame + // Finish up with last surface used in the list of RenderViews + if (beganDrawing) { + SurfaceLocker surfaceLock(submissionData.surface); + // Finish up with last surface used in the list of RenderViews + m_submissionContext->endDrawing(submissionData.lastBoundFBOId == m_submissionContext->defaultFBO() && surfaceLock.isSurfaceValid()); + } +} + +// Called by RenderViewJobs +// When the frameQueue is complete and we are using a renderThread +// we allow the render thread to proceed +void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder) +{ + QMutexLocker locker(m_renderQueue->mutex()); // Prevent out of order execution + // We cannot use a lock free primitive here because: + // - QVector is not thread safe + // - Even if the insert is made correctly, the isFrameComplete call + // could be invalid since depending on the order of execution + // the counter could be complete but the renderview not yet added to the + // buffer depending on whichever order the cpu decides to process this + const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder); + locker.unlock(); // We're done protecting the queue at this point + if (isQueueComplete) { + if (m_renderThread && m_running.load()) + Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0); + m_submitRenderViewsSemaphore.release(1); + } +} + +bool Renderer::canRender() const +{ + // Make sure that we've not been told to terminate + if (m_renderThread && !m_running.load()) { + qCDebug(Rendering) << "RenderThread termination requested whilst waiting"; + return false; + } + + // TO DO: Check if all surfaces have been destroyed... + // It may be better if the last window to be closed trigger a call to shutdown + // Rather than having checks for the surface everywhere + + return true; +} + +bool Renderer::isReadyToSubmit() +{ + // If we are using a render thread, make sure that + // we've been told to render before rendering + if (m_renderThread) { // Prevent ouf of order execution + m_submitRenderViewsSemaphore.acquire(1); + + // Check if shutdown has been requested + if (m_running.load() == 0) + return false; + + // When using Thread rendering, the semaphore should only + // be released when the frame queue is complete and there's + // something to render + // The case of shutdown should have been handled just before + Q_ASSERT(m_renderQueue->isFrameQueueComplete()); + } + return true; +} + +// Main thread +QVariant Renderer::executeCommand(const QStringList &args) +{ +#if QT_CONFIG(qt3d_profile_jobs) + return m_commandExecuter->executeCommand(args); +#else + Q_UNUSED(args); +#endif + return QVariant(); +} + +/*! + \internal + Called in the context of the aspect thread from QRenderAspect::onRegistered +*/ +void Renderer::setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) +{ + QMutexLocker locker(&m_offscreenSurfaceMutex); + m_offscreenHelper = helper; +} + +QSurfaceFormat Renderer::format() +{ + return m_format; +} + +// When this function is called, we must not be processing the commands for frame n+1 +void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderViews) +{ + OpenGLVertexArrayObject *vao = nullptr; + QHash<HVao, bool> updatedTable; + + for (RenderView *rv: renderViews) { + const QVector<RenderCommand *> commands = rv->commands(); + for (RenderCommand *command : commands) { + // Update/Create VAO + if (command->m_type == RenderCommand::Draw) { + Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command->m_geometry); + GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command->m_geometryRenderer); + Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command->m_shader); + + // We should never have inserted a command for which these are null + // in the first place + Q_ASSERT(rGeometry && rGeometryRenderer && shader); + + // The VAO should be created only once for a QGeometry and a ShaderProgram + // Manager should have a VAO Manager that are indexed by QMeshData and Shader + // RenderCommand should have a handle to the corresponding VAO for the Mesh and Shader + HVao vaoHandle; + + // Create VAO or return already created instance associated with command shader/geometry + // (VAO is emulated if not supported) + createOrUpdateVAO(command, &vaoHandle, &vao); + command->m_vao = vaoHandle; + + // Avoids redoing the same thing for the same VAO + if (!updatedTable.contains(vaoHandle)) { + updatedTable.insert(vaoHandle, true); + + // Do we have any attributes that are dirty ? + const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, command); + + // If true, we need to reupload all attributes to set the VAO + // Otherwise only dirty attributes will be updates + const bool requiresFullVAOUpdate = (!vao->isSpecified()) || (rGeometry->isDirty() || rGeometryRenderer->isDirty()); + + // Append dirty Geometry to temporary vector + // so that its dirtiness can be unset later + if (rGeometry->isDirty()) + m_dirtyGeometry.push_back(rGeometry); + + if (!command->m_attributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { + Profiling::GLTimeRecorder recorder(Profiling::VAOUpload); + // Activate shader + m_submissionContext->activateShader(shader->dna()); + // Bind VAO + vao->bind(); + // Update or set Attributes and Buffers for the given rGeometry and Command + // Note: this fills m_dirtyAttributes as well + if (updateVAOWithAttributes(rGeometry, command, shader, requiresFullVAOUpdate)) + vao->setSpecified(true); + } + } + + // Unset dirtiness on rGeometryRenderer only + // The rGeometry may be shared by several rGeometryRenderer + // so we cannot unset its dirtiness at this point + if (rGeometryRenderer->isDirty()) + rGeometryRenderer->unsetDirty(); + + // Prepare the ShaderParameterPack based on the active uniforms of the shader + shader->prepareUniforms(command->m_parameterPack); + + // TO DO: The step below could be performed by the RenderCommand builder job + { // Scoped to show extent + command->m_isValid = !command->m_attributes.empty(); + if (!command->m_isValid) + continue; + + // Update the draw command with what's going to be needed for the drawing + uint primitiveCount = rGeometryRenderer->vertexCount(); + uint estimatedCount = 0; + Attribute *indexAttribute = nullptr; + Attribute *indirectAttribute = nullptr; + + const QVector<Qt3DCore::QNodeId> attributeIds = rGeometry->attributes(); + for (Qt3DCore::QNodeId attributeId : attributeIds) { + Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + switch (attribute->attributeType()) { + case QAttribute::IndexAttribute: + indexAttribute = attribute; + break; + case QAttribute::DrawIndirectAttribute: + indirectAttribute = attribute; + break; + case QAttribute::VertexAttribute: { + if (command->m_attributes.contains(attribute->nameId())) + estimatedCount = qMax(attribute->count(), estimatedCount); + break; + } + default: + Q_UNREACHABLE(); + break; + } + } + + command->m_drawIndexed = (indexAttribute != nullptr); + command->m_drawIndirect = (indirectAttribute != nullptr); + + // Update the draw command with all the information required for the drawing + if (command->m_drawIndexed) { + command->m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); + command->m_indexAttributeByteOffset = indexAttribute->byteOffset() + rGeometryRenderer->indexBufferByteOffset(); + } + + // Note: we only care about the primitiveCount when using direct draw calls + // For indirect draw calls it is assumed the buffer was properly set already + if (command->m_drawIndirect) { + command->m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); + command->m_indirectDrawBuffer = m_nodesManager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); + } else { + // Use the count specified by the GeometryRender + // If not specify use the indexAttribute count if present + // Otherwise tries to use the count from the attribute with the highest count + if (primitiveCount == 0) { + if (indexAttribute) + primitiveCount = indexAttribute->count(); + else + primitiveCount = estimatedCount; + } + } + + command->m_primitiveCount = primitiveCount; + command->m_primitiveType = rGeometryRenderer->primitiveType(); + command->m_primitiveRestartEnabled = rGeometryRenderer->primitiveRestartEnabled(); + command->m_restartIndexValue = rGeometryRenderer->restartIndexValue(); + command->m_firstInstance = rGeometryRenderer->firstInstance(); + command->m_instanceCount = rGeometryRenderer->instanceCount(); + command->m_firstVertex = rGeometryRenderer->firstVertex(); + command->m_indexOffset = rGeometryRenderer->indexOffset(); + command->m_verticesPerPatch = rGeometryRenderer->verticesPerPatch(); + } // scope + } else if (command->m_type == RenderCommand::Compute) { + Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command->m_shader); + Q_ASSERT(shader); + + // Prepare the ShaderParameterPack based on the active uniforms of the shader + shader->prepareUniforms(command->m_parameterPack); + } + } + } + + // Make sure we leave nothing bound + if (vao) + vao->release(); + + // Unset dirtiness on Geometry and Attributes + // Note: we cannot do it in the loop above as we want to be sure that all + // the VAO which reference the geometry/attributes are properly updated + for (Attribute *attribute : qAsConst(m_dirtyAttributes)) + attribute->unsetDirty(); + m_dirtyAttributes.clear(); + + for (Geometry *geometry : qAsConst(m_dirtyGeometry)) + geometry->unsetDirty(); + m_dirtyGeometry.clear(); +} + +// Executed in a job +void Renderer::lookForAbandonedVaos() +{ + const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles(); + for (const HVao &handle : activeVaos) { + OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(handle); + + // Make sure to only mark VAOs for deletion that were already created + // (ignore those that might be currently under construction in the render thread) + if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_nodesManager->shaderManager())) { + m_abandonedVaosMutex.lock(); + m_abandonedVaos.push_back(handle); + m_abandonedVaosMutex.unlock(); + } + } +} + +// Executed in a job +void Renderer::lookForDirtyBuffers() +{ + const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles(); + for (const HBuffer &handle: activeBufferHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + if (buffer->isDirty()) + m_dirtyBuffers.push_back(handle); + } +} + +void Renderer::lookForDownloadableBuffers() +{ + m_downloadableBuffers.clear(); + const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles(); + for (const HBuffer &handle : activeBufferHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + if (buffer->access() & QBuffer::Read) + m_downloadableBuffers.push_back(handle); + } +} + +// Executed in a job +void Renderer::lookForDirtyTextures() +{ + const QVector<HTexture> activeTextureHandles = m_nodesManager->textureManager()->activeHandles(); + for (const HTexture &handle: activeTextureHandles) { + Texture *texture = m_nodesManager->textureManager()->data(handle); + // Dirty meaning that something has changed on the texture + // either properties, parameters, generator or a texture image + if (texture->dirtyFlags() != Texture::NotDirty) + m_dirtyTextures.push_back(handle); + } +} + +// Executed in a job +void Renderer::reloadDirtyShaders() +{ + Q_ASSERT(isRunning()); + const QVector<HTechnique> activeTechniques = m_nodesManager->techniqueManager()->activeHandles(); + const QVector<HShaderBuilder> activeBuilders = m_nodesManager->shaderBuilderManager()->activeHandles(); + for (const HTechnique &techniqueHandle : activeTechniques) { + Technique *technique = m_nodesManager->techniqueManager()->data(techniqueHandle); + // If api of the renderer matches the one from the technique + if (technique->isCompatibleWithRenderer()) { + const auto passIds = technique->renderPasses(); + for (const QNodeId passId : passIds) { + RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId); + HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram()); + Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle); + + ShaderBuilder *shaderBuilder = nullptr; + for (const HShaderBuilder &builderHandle : activeBuilders) { + ShaderBuilder *builder = m_nodesManager->shaderBuilderManager()->data(builderHandle); + if (builder->shaderProgramId() == shader->peerId()) { + shaderBuilder = builder; + break; + } + } + + if (shaderBuilder) { + shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter()); + + for (int i = 0; i <= ShaderBuilder::Compute; i++) { + const auto builderType = static_cast<ShaderBuilder::ShaderType>(i); + if (!shaderBuilder->shaderGraph(builderType).isValid()) + continue; + + if (shaderBuilder->isShaderCodeDirty(builderType)) { + shaderBuilder->generateCode(builderType); + } + + QShaderProgram::ShaderType shaderType = QShaderProgram::Vertex; + switch (builderType) { + case ShaderBuilder::Vertex: + shaderType = QShaderProgram::Vertex; + break; + case ShaderBuilder::TessellationControl: + shaderType = QShaderProgram::TessellationControl; + break; + case ShaderBuilder::TessellationEvaluation: + shaderType = QShaderProgram::TessellationEvaluation; + break; + case ShaderBuilder::Geometry: + shaderType = QShaderProgram::Geometry; + break; + case ShaderBuilder::Fragment: + shaderType = QShaderProgram::Fragment; + break; + case ShaderBuilder::Compute: + shaderType = QShaderProgram::Compute; + break; + } + + const auto code = shaderBuilder->shaderCode(builderType); + shader->setShaderCode(shaderType, code); + } + } + + if (Q_UNLIKELY(shader->hasPendingNotifications())) + shader->submitPendingNotifications(); + // If the shader hasn't be loaded, load it + if (shader != nullptr && !shader->isLoaded()) + loadShader(shader); + } + } + } +} + +// Render Thread +void Renderer::updateGLResources() +{ + { + Profiling::GLTimeRecorder recorder(Profiling::BufferUpload); + const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers); + for (const HBuffer &handle: dirtyBufferHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + // Forces creation if it doesn't exit + // Also note the binding point doesn't really matter here, we just upload data + if (!m_submissionContext->hasGLBufferForBuffer(buffer)) + m_submissionContext->glBufferForRenderBuffer(buffer, GLBuffer::ArrayBuffer); + // Update the glBuffer data + m_submissionContext->updateBuffer(buffer); + buffer->unsetDirty(); + } + } + + { + Profiling::GLTimeRecorder recorder(Profiling::TextureUpload); + const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures); + for (const HTexture &handle: activeTextureHandles) { + Texture *texture = m_nodesManager->textureManager()->data(handle); + // Upload/Update texture + updateTexture(texture); + } + } + // When Textures are cleaned up, their id is saved + // so that they can be cleaned up in the render thread + // Note: we perform this step in second so that the previous updateTexture call + // has a chance to find a shared texture + const QVector<Qt3DCore::QNodeId> cleanedUpTextureIds = m_nodesManager->textureManager()->takeTexturesIdsToCleanup(); + for (const Qt3DCore::QNodeId textureCleanedUpId: cleanedUpTextureIds) { + cleanupTexture(m_nodesManager->textureManager()->lookupResource(textureCleanedUpId)); + // We can really release the texture at this point + m_nodesManager->textureManager()->releaseResource(textureCleanedUpId); + } +} + +// Render Thread +void Renderer::updateTexture(Texture *texture) +{ + // Check that the current texture images are still in place, if not, do not update + const bool isValid = texture->isValid(); + if (!isValid) + return; + + // For implementing unique, non-shared, non-cached textures. + // for now, every texture is shared by default + + bool isUnique = false; + + // TO DO: Update the vector once per frame (or in a job) + const QVector<HAttachment> activeRenderTargetOutputs = m_nodesManager->attachmentManager()->activeHandles(); + // A texture is unique if it's being reference by a render target output + for (const HAttachment &attachmentHandle : activeRenderTargetOutputs) { + RenderTargetOutput *attachment = m_nodesManager->attachmentManager()->data(attachmentHandle); + if (attachment->textureUuid() == texture->peerId()) { + isUnique = true; + break; + } + } + + // Try to find the associated GLTexture for the backend Texture + GLTextureManager *glTextureManager = m_nodesManager->glTextureManager(); + GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId()); + + // No GLTexture associated yet -> create it + if (glTexture == nullptr) { + if (isUnique) + glTextureManager->createUnique(texture); + else + glTextureManager->getOrCreateShared(texture); + texture->unsetDirty(); + return; + } + + // if this texture is a shared texture, we might need to look for a new TextureImpl + // and abandon the old one + if (glTextureManager->isShared(glTexture)) { + glTextureManager->abandon(glTexture, texture); + // Check if a shared texture should become unique + if (isUnique) + glTextureManager->createUnique(texture); + else + glTextureManager->getOrCreateShared(texture); + texture->unsetDirty(); + return; + } + + // this texture node is the only one referring to the GLTexture. + // we could thus directly modify the texture. Instead, for non-unique textures, + // we first see if there is already a matching texture present. + if (!isUnique) { + GLTexture *newSharedTex = glTextureManager->findMatchingShared(texture); + if (newSharedTex && newSharedTex != glTexture) { + glTextureManager->abandon(glTexture, texture); + glTextureManager->adoptShared(newSharedTex, texture); + texture->unsetDirty(); + return; + } + } + + // we hold a reference to a unique or exclusive access to a shared texture + // we can thus modify the texture directly. + const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags(); + + if (dirtyFlags.testFlag(Texture::DirtyProperties) && + !glTextureManager->setProperties(glTexture, texture->properties())) + qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setProperties failed, should be non-shared"; + + if (dirtyFlags.testFlag(Texture::DirtyParameters) && + !glTextureManager->setParameters(glTexture, texture->parameters())) + qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setParameters failed, should be non-shared"; + + // Will make the texture requestUpload + if (dirtyFlags.testFlag(Texture::DirtyImageGenerators) && + !glTextureManager->setImages(glTexture, texture->textureImages())) + qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerators failed, should be non-shared"; + + // Will make the texture requestUpload + if (dirtyFlags.testFlag(Texture::DirtyDataGenerator) && + !glTextureManager->setGenerator(glTexture, texture->dataGenerator())) + qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerator failed, should be non-shared"; + + // Unset the dirty flag on the texture + texture->unsetDirty(); +} + +// Render Thread +void Renderer::cleanupTexture(const Texture *texture) +{ + GLTextureManager *glTextureManager = m_nodesManager->glTextureManager(); + GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId()); + + if (glTexture != nullptr) + glTextureManager->abandon(glTexture, texture); +} + +void Renderer::downloadGLBuffers() +{ + lookForDownloadableBuffers(); + const QVector<HBuffer> downloadableHandles = std::move(m_downloadableBuffers); + for (const HBuffer &handle : downloadableHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + QByteArray content = m_submissionContext->downloadBufferContent(buffer); + m_sendBufferCaptureJob->addRequest(QPair<Buffer*, QByteArray>(buffer, content)); + } +} + +// Happens in RenderThread context when all RenderViewJobs are done +// Returns the id of the last bound FBO +Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Render::RenderView *> &renderViews) +{ + QElapsedTimer timer; + quint64 queueElapsed = 0; + timer.start(); + + const int renderViewsCount = renderViews.size(); + quint64 frameElapsed = queueElapsed; + m_lastFrameCorrect.store(1); // everything fine until now..... + + qCDebug(Memory) << Q_FUNC_INFO << "rendering frame "; + + // We might not want to render on the default FBO + uint lastBoundFBOId = m_submissionContext->boundFrameBufferObject(); + QSurface *surface = nullptr; + QSurface *previousSurface = nullptr; + for (const Render::RenderView *rv: renderViews) { + previousSurface = rv->surface(); + if (previousSurface) + break; + } + QSurface *lastUsedSurface = nullptr; + + for (int i = 0; i < renderViewsCount; ++i) { + // Initialize GraphicsContext for drawing + // If the RenderView has a RenderStateSet defined + const RenderView *renderView = renderViews.at(i); + + // Check if using the same surface as the previous RenderView. + // If not, we have to free up the context from the previous surface + // and make the context current on the new surface + surface = renderView->surface(); + SurfaceLocker surfaceLock(surface); + + // TO DO: Make sure that the surface we are rendering too has not been unset + + // For now, if we do not have a surface, skip this renderview + // TODO: Investigate if it's worth providing a fallback offscreen surface + // to use when surface is null. Or if we should instead expose an + // offscreensurface to Qt3D. + if (!surface || !surfaceLock.isSurfaceValid()) { + m_lastFrameCorrect.store(0); + continue; + } + + lastUsedSurface = surface; + const bool surfaceHasChanged = surface != previousSurface; + + if (surfaceHasChanged && previousSurface) { + const bool swapBuffers = (lastBoundFBOId == m_submissionContext->defaultFBO()) && PlatformSurfaceFilter::isSurfaceValid(previousSurface); + // We only call swap buffer if we are sure the previous surface is still valid + m_submissionContext->endDrawing(swapBuffers); + } + + if (surfaceHasChanged) { + // If we can't make the context current on the surface, skip to the + // next RenderView. We won't get the full frame but we may get something + if (!m_submissionContext->beginDrawing(surface)) { + qWarning() << "Failed to make OpenGL context current on surface"; + m_lastFrameCorrect.store(0); + continue; + } + + previousSurface = surface; + lastBoundFBOId = m_submissionContext->boundFrameBufferObject(); + } + + // Apply Memory Barrier if needed + if (renderView->memoryBarrier() != QMemoryBarrier::None) + m_submissionContext->memoryBarrier(renderView->memoryBarrier()); + + // Note: the RenderStateSet is allocated once per RV if needed + // and it contains a list of StateVariant value types + RenderStateSet *renderViewStateSet = renderView->stateSet(); + + { + Profiling::GLTimeRecorder recorder(Profiling::StateUpdate); + // Set the RV state if not null, + if (renderViewStateSet != nullptr) + m_submissionContext->setCurrentStateSet(renderViewStateSet); + else + m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet); + } + + // Set RenderTarget ... + // Activate RenderTarget + { + Profiling::GLTimeRecorder recorder(Profiling::RenderTargetUpdate); + m_submissionContext->activateRenderTarget(renderView->renderTargetId(), + renderView->attachmentPack(), + lastBoundFBOId); + } + + { + Profiling::GLTimeRecorder recorder(Profiling::ClearBuffer); + // set color, depth, stencil clear values (only if needed) + auto clearBufferTypes = renderView->clearTypes(); + if (clearBufferTypes & QClearBuffers::ColorBuffer) { + const QVector4D vCol = renderView->globalClearColorBufferInfo().clearColor; + m_submissionContext->clearColor(QColor::fromRgbF(vCol.x(), vCol.y(), vCol.z(), vCol.w())); + } + if (clearBufferTypes & QClearBuffers::DepthBuffer) + m_submissionContext->clearDepthValue(renderView->clearDepthValue()); + if (clearBufferTypes & QClearBuffers::StencilBuffer) + m_submissionContext->clearStencilValue(renderView->clearStencilValue()); + + // Clear BackBuffer + m_submissionContext->clearBackBuffer(clearBufferTypes); + + // if there are ClearColors set for different draw buffers, + // clear each of these draw buffers individually now + const QVector<ClearBufferInfo> clearDrawBuffers = renderView->specificClearColorBufferInfo(); + for (const ClearBufferInfo &clearBuffer : clearDrawBuffers) + m_submissionContext->clearBufferf(clearBuffer.drawBufferIndex, clearBuffer.clearColor); + } + + // Set the Viewport + m_submissionContext->setViewport(renderView->viewport(), renderView->surfaceSize() * renderView->devicePixelRatio()); + + // Execute the render commands + if (!executeCommandsSubmission(renderView)) + m_lastFrameCorrect.store(0); // something went wrong; make sure to render the next frame! + + // executeCommandsSubmission takes care of restoring the stateset to the value + // of gc->currentContext() at the moment it was called (either + // renderViewStateSet or m_defaultRenderStateSet) + if (!renderView->renderCaptureNodeId().isNull()) { + const QRenderCaptureRequest request = renderView->renderCaptureRequest(); + const QSize size = m_submissionContext->renderTargetSize(renderView->surfaceSize() * renderView->devicePixelRatio()); + QRect rect(QPoint(0, 0), size); + if (!request.rect.isEmpty()) + rect = rect.intersected(request.rect); + QImage image; + if (!rect.isEmpty()) { + // Bind fbo as read framebuffer + m_submissionContext->bindFramebuffer(m_submissionContext->activeFBO(), GraphicsHelperInterface::FBORead); + image = m_submissionContext->readFramebuffer(rect); + } else { + qWarning() << "Requested capture rectangle is outside framebuffer"; + } + Render::RenderCapture *renderCapture = + static_cast<Render::RenderCapture*>(m_nodesManager->frameGraphManager()->lookupNode(renderView->renderCaptureNodeId())); + renderCapture->addRenderCapture(request.captureId, image); + addRenderCaptureSendRequest(renderView->renderCaptureNodeId()); + } + + if (renderView->isDownloadBuffersEnable()) + downloadGLBuffers(); + + // Perform BlitFramebuffer operations + if (renderView->hasBlitFramebufferInfo()) { + const auto &blitFramebufferInfo = renderView->blitFrameBufferInfo(); + const QNodeId inputTargetId = blitFramebufferInfo.sourceRenderTargetId; + const QNodeId outputTargetId = blitFramebufferInfo.destinationRenderTargetId; + const QRect inputRect = blitFramebufferInfo.sourceRect; + const QRect outputRect = blitFramebufferInfo.destinationRect; + const QRenderTargetOutput::AttachmentPoint inputAttachmentPoint = blitFramebufferInfo.sourceAttachmentPoint; + const QRenderTargetOutput::AttachmentPoint outputAttachmentPoint = blitFramebufferInfo.destinationAttachmentPoint; + const QBlitFramebuffer::InterpolationMethod interpolationMethod = blitFramebufferInfo.interpolationMethod; + m_submissionContext->blitFramebuffer(inputTargetId, outputTargetId, inputRect, outputRect, lastBoundFBOId, + inputAttachmentPoint, outputAttachmentPoint, + interpolationMethod); + } + + + frameElapsed = timer.elapsed() - frameElapsed; + qCDebug(Rendering) << Q_FUNC_INFO << "Submitted Renderview " << i + 1 << "/" << renderViewsCount << "in " << frameElapsed << "ms"; + frameElapsed = timer.elapsed(); + } + + // Bind lastBoundFBOId back. Needed also in threaded mode. + // lastBoundFBOId != m_graphicsContext->activeFBO() when the last FrameGraph leaf node/renderView + // contains RenderTargetSelector/RenderTarget + if (lastBoundFBOId != m_submissionContext->activeFBO()) + m_submissionContext->bindFramebuffer(lastBoundFBOId, GraphicsHelperInterface::FBOReadAndDraw); + + // Reset state and call doneCurrent if the surface + // is valid and was actually activated + if (surface && m_submissionContext->hasValidGLHelper()) { + // Reset state to the default state if the last stateset is not the + // defaultRenderStateSet + if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet) + m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet); + } + + queueElapsed = timer.elapsed() - queueElapsed; + qCDebug(Rendering) << Q_FUNC_INFO << "Submission of Queue in " << queueElapsed << "ms <=> " << queueElapsed / renderViewsCount << "ms per RenderView <=> Avg " << 1000.0f / (queueElapsed * 1.0f/ renderViewsCount * 1.0f) << " RenderView/s"; + qCDebug(Rendering) << Q_FUNC_INFO << "Submission Completed in " << timer.elapsed() << "ms"; + + // Stores the necessary information to safely perform + // the last swap buffer call + ViewSubmissionResultData resultData; + resultData.lastBoundFBOId = lastBoundFBOId; + resultData.surface = lastUsedSurface; + + return resultData; +} + +void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node) +{ + Q_UNUSED(node); + m_dirtyBits.marked |= changes; +} + +Renderer::BackendNodeDirtySet Renderer::dirtyBits() +{ + return m_dirtyBits.marked; +} + +#if defined(QT_BUILD_INTERNAL) +void Renderer::clearDirtyBits(BackendNodeDirtySet changes) +{ + m_dirtyBits.remaining &= ~changes; + m_dirtyBits.marked &= ~changes; +} +#endif + +bool Renderer::shouldRender() +{ + // Only render if something changed during the last frame, or the last frame + // was not rendered successfully (or render-on-demand is disabled) + return (m_settings->renderPolicy() == QRenderSettings::Always + || m_dirtyBits.marked != 0 + || m_dirtyBits.remaining != 0 + || !m_lastFrameCorrect.load()); +} + +void Renderer::skipNextFrame() +{ + Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always); + + // make submitRenderViews() actually run + m_renderQueue->setNoRender(); + m_submitRenderViewsSemaphore.release(1); +} + +// Waits to be told to create jobs for the next frame +// Called by QRenderAspect jobsToExecute context of QAspectThread +// Returns all the jobs (and with proper dependency chain) required +// for the rendering of the scene +QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() +{ + QVector<QAspectJobPtr> renderBinJobs; + + // Create the jobs to build the frame + const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs(); + + // Remove previous dependencies + m_calculateBoundingVolumeJob->removeDependency(QWeakPointer<QAspectJob>()); + m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>()); + + // Set dependencies + for (const QAspectJobPtr &bufferJob : bufferJobs) + m_calculateBoundingVolumeJob->addDependency(bufferJob); + + m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot()); + + const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining; + m_dirtyBits.marked = 0; + m_dirtyBits.remaining = 0; + BackendNodeDirtySet notCleared = 0; + + // Add jobs + const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty; + if (entitiesEnabledDirty) { + renderBinJobs.push_back(m_updateTreeEnabledJob); + // This dependency is added here because we clear all dependencies + // at the start of this function. + m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob); + } + + if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) { + renderBinJobs.push_back(m_worldTransformJob); + renderBinJobs.push_back(m_updateWorldBoundingVolumeJob); + renderBinJobs.push_back(m_updateShaderDataTransformJob); + } + + if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty) { + renderBinJobs.push_back(m_calculateBoundingVolumeJob); + renderBinJobs.push_back(m_updateMeshTriangleListJob); + } + + if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty || + dirtyBitsForFrame & AbstractRenderer::TransformDirty) { + renderBinJobs.push_back(m_expandBoundingVolumeJob); + } + + m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints()); + renderBinJobs.push_back(m_updateSkinningPaletteJob); + renderBinJobs.push_back(m_updateLevelOfDetailJob); + renderBinJobs.push_back(m_cleanupJob); + renderBinJobs.push_back(m_sendRenderCaptureJob); + renderBinJobs.push_back(m_sendBufferCaptureJob); + renderBinJobs.append(bufferJobs); + + // Jobs to prepare GL Resource upload + renderBinJobs.push_back(m_vaoGathererJob); + + if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty) + renderBinJobs.push_back(m_bufferGathererJob); + + if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty) { + renderBinJobs.push_back(m_syncTextureLoadingJob); + renderBinJobs.push_back(m_textureGathererJob); + } + + + // Layer cache is dependent on layers, layer filters and the enabled flag + // on entities + const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty; + const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty; + const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty; + + QMutexLocker lock(m_renderQueue->mutex()); + if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case) + // Traverse the current framegraph. For each leaf node create a + // RenderView and set its configuration then create a job to + // populate the RenderView with a set of RenderCommands that get + // their details from the RenderNodes that are visible to the + // Camera selected by the framegraph configuration + FrameGraphVisitor visitor(m_nodesManager->frameGraphManager()); + const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot()); + + // Remove leaf nodes that no longer exist from cache + const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys(); + for (FrameGraphNode *leafNode : keys) { + if (!fgLeaves.contains(leafNode)) + m_cache.leafNodeCache.remove(leafNode); + } + + const int fgBranchCount = fgLeaves.size(); + for (int i = 0; i < fgBranchCount; ++i) { + RenderViewBuilder builder(fgLeaves.at(i), i, this); + builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt); + builder.setMaterialGathererCacheNeedsToBeRebuilt(materialDirty); + builder.prepareJobs(); + renderBinJobs.append(builder.buildJobHierachy()); + } + + // Set target number of RenderViews + m_renderQueue->setTargetRenderViewCount(fgBranchCount); + } else { + // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later + // if none of those jobs are started this frame + notCleared |= AbstractRenderer::EntityEnabledDirty; + notCleared |= AbstractRenderer::LayersDirty; + } + + if (isRunning() && m_submissionContext->isInitialized()) { + if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty ) + renderBinJobs.push_back(m_filterCompatibleTechniqueJob); + if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty) + renderBinJobs.push_back(m_introspectShaderJob); + } else { + notCleared |= AbstractRenderer::TechniquesDirty; + notCleared |= AbstractRenderer::ShadersDirty; + } + + m_dirtyBits.remaining = dirtyBitsForFrame & notCleared; + + return renderBinJobs; +} + +QAspectJobPtr Renderer::pickBoundingVolumeJob() +{ + // Set values on pickBoundingVolumeJob + RenderSettings *renderSetting = settings(); + if (renderSetting != nullptr) { + m_pickBoundingVolumeJob->setRenderSettings(renderSetting); + m_pickBoundingVolumeJob->setFrameGraphRoot(frameGraphRoot()); + m_pickBoundingVolumeJob->setMouseEvents(pendingPickingEvents()); + m_pickBoundingVolumeJob->setKeyEvents(pendingKeyEvents()); + } + + return m_pickBoundingVolumeJob; +} + +QAspectJobPtr Renderer::rayCastingJob() +{ + // Set values on rayCastingJob + RenderSettings *renderSetting = settings(); + if (renderSetting != nullptr) { + m_rayCastingJob->setRenderSettings(renderSetting); + m_rayCastingJob->setFrameGraphRoot(frameGraphRoot()); + } + + return m_rayCastingJob; +} + +QAspectJobPtr Renderer::syncTextureLoadingJob() +{ + return m_syncTextureLoadingJob; +} + +QAspectJobPtr Renderer::expandBoundingVolumeJob() +{ + return m_expandBoundingVolumeJob; +} + +QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const +{ + return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data()); +} + +// Called by executeCommands +void Renderer::performDraw(RenderCommand *command) +{ + // Indirect Draw Calls + if (command->m_drawIndirect) { + + // Bind the indirect draw buffer + Buffer *indirectDrawBuffer = m_nodesManager->bufferManager()->data(command->m_indirectDrawBuffer); + if (Q_UNLIKELY(indirectDrawBuffer == nullptr)) { + qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve Buffer"; + return; + } + + // Get GLBuffer from Buffer; + GLBuffer *indirectDrawGLBuffer = m_submissionContext->glBufferForRenderBuffer(indirectDrawBuffer, GLBuffer::DrawIndirectBuffer); + if (Q_UNLIKELY(indirectDrawGLBuffer == nullptr)) { + qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve GLBuffer"; + return; + } + + // Bind GLBuffer + const bool successfullyBound = indirectDrawGLBuffer->bind(m_submissionContext.data(), GLBuffer::DrawIndirectBuffer); + + if (Q_LIKELY(successfullyBound)) { + // TO DO: Handle multi draw variants if attribute count > 1 + if (command->m_drawIndexed) { + m_submissionContext->drawElementsIndirect(command->m_primitiveType, + command->m_indexAttributeDataType, + reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset))); + } else { + m_submissionContext->drawArraysIndirect(command->m_primitiveType, + reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset))); + } + } else { + qWarning() << "Failed to bind IndirectDrawBuffer"; + } + + } else { // Direct Draw Calls + + // TO DO: Add glMulti Draw variants + if (command->m_primitiveType == QGeometryRenderer::Patches) + m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch); + + if (command->m_primitiveRestartEnabled) + m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue); + + // TO DO: Add glMulti Draw variants + if (command->m_drawIndexed) { + Profiling::GLTimeRecorder recorder(Profiling::DrawElement); + m_submissionContext->drawElementsInstancedBaseVertexBaseInstance(command->m_primitiveType, + command->m_primitiveCount, + command->m_indexAttributeDataType, + reinterpret_cast<void*>(quintptr(command->m_indexAttributeByteOffset)), + command->m_instanceCount, + command->m_indexOffset, + command->m_firstVertex); + } else { + Profiling::GLTimeRecorder recorder(Profiling::DrawArray); + m_submissionContext->drawArraysInstancedBaseInstance(command->m_primitiveType, + command->m_firstInstance, + command->m_primitiveCount, + command->m_instanceCount, + command->m_firstVertex); + } + } + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + int err = m_graphicsContext->openGLContext()->functions()->glGetError(); + if (err) + qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16); +#endif + + if (command->m_primitiveRestartEnabled) + m_submissionContext->disablePrimitiveRestart(); +} + +void Renderer::performCompute(const RenderView *, RenderCommand *command) +{ + { + Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); + m_submissionContext->activateShader(command->m_shaderDna); + } + { + Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); + m_submissionContext->setParameters(command->m_parameterPack); + } + { + Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute); + m_submissionContext->dispatchCompute(command->m_workGroups[0], + command->m_workGroups[1], + command->m_workGroups[2]); + } + // HACK: Reset the compute flag to dirty + m_dirtyBits.marked |= AbstractRenderer::ComputeDirty; + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + int err = m_graphicsContext->openGLContext()->functions()->glGetError(); + if (err) + qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16); +#endif +} + +void Renderer::createOrUpdateVAO(RenderCommand *command, + HVao *previousVaoHandle, + OpenGLVertexArrayObject **vao) +{ + const VAOIdentifier vaoKey(command->m_geometry, command->m_shader); + + VAOManager *vaoManager = m_nodesManager->vaoManager(); + command->m_vao = vaoManager->lookupHandle(vaoKey); + + if (command->m_vao.isNull()) { + qCDebug(Rendering) << Q_FUNC_INFO << "Allocating new VAO"; + command->m_vao = vaoManager->getOrAcquireHandle(vaoKey); + vaoManager->data(command->m_vao)->create(m_submissionContext.data(), vaoKey); + } + + if (*previousVaoHandle != command->m_vao) { + *previousVaoHandle = command->m_vao; + *vao = vaoManager->data(command->m_vao); + } + Q_ASSERT(*vao); +} + +// Called by RenderView->submit() in RenderThread context +// Returns true, if all RenderCommands were sent to the GPU +bool Renderer::executeCommandsSubmission(const RenderView *rv) +{ + bool allCommandsIssued = true; + + // Render drawing commands + const QVector<RenderCommand *> commands = rv->commands(); + + // Use the graphicscontext to submit the commands to the underlying + // graphics API (OpenGL) + + // Save the RenderView base stateset + RenderStateSet *globalState = m_submissionContext->currentStateSet(); + OpenGLVertexArrayObject *vao = nullptr; + + for (RenderCommand *command : qAsConst(commands)) { + + if (command->m_type == RenderCommand::Compute) { // Compute Call + performCompute(rv, command); + } else { // Draw Command + // Check if we have a valid command that can be drawn + if (!command->m_isValid) { + allCommandsIssued = false; + continue; + } + + vao = m_nodesManager->vaoManager()->data(command->m_vao); + + // something may have went wrong when initializing the VAO + if (!vao->isSpecified()) { + allCommandsIssued = false; + continue; + } + + { + Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); + //// We activate the shader here + if (!m_submissionContext->activateShader(command->m_shaderDna)) { + allCommandsIssued = false; + continue; + } + } + + { + Profiling::GLTimeRecorder recorder(Profiling::VAOUpdate); + // Bind VAO + vao->bind(); + } + + { + Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); + //// Update program uniforms + if (!m_submissionContext->setParameters(command->m_parameterPack)) { + allCommandsIssued = false; + // If we have failed to set uniform (e.g unable to bind a texture) + // we won't perform the draw call which could show invalid content + continue; + } + } + + //// OpenGL State + // TO DO: Make states not dependendent on their backend node for this step + // Set state + RenderStateSet *localState = command->m_stateSet; + + + { + Profiling::GLTimeRecorder recorder(Profiling::StateUpdate); + // Merge the RenderCommand state with the globalState of the RenderView + // Or restore the globalState if no stateSet for the RenderCommand + if (localState != nullptr) { + command->m_stateSet->merge(globalState); + m_submissionContext->setCurrentStateSet(command->m_stateSet); + } else { + m_submissionContext->setCurrentStateSet(globalState); + } + } + // All Uniforms for a pass are stored in the QUniformPack of the command + // Uniforms for Effect, Material and Technique should already have been correctly resolved + // at that point + + //// Draw Calls + performDraw(command); + } + } // end of RenderCommands loop + + // We cache the VAO and release it only at the end of the exectute frame + // We try to minimize VAO binding between RenderCommands + if (vao) + vao->release(); + + // Reset to the state we were in before executing the render commands + m_submissionContext->setCurrentStateSet(globalState); + + return allCommandsIssued; +} + +bool Renderer::updateVAOWithAttributes(Geometry *geometry, + RenderCommand *command, + Shader *shader, + bool forceUpdate) +{ + m_dirtyAttributes.reserve(m_dirtyAttributes.size() + geometry->attributes().size()); + const auto attributeIds = geometry->attributes(); + + for (QNodeId attributeId : attributeIds) { + // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager + Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + + if (attribute == nullptr) + return false; + + Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attribute->bufferId()); + + // Buffer update was already performed at this point + // Just make sure the attribute reference a valid buffer + if (buffer == nullptr) + return false; + + // Index Attribute + bool attributeWasDirty = false; + if (attribute->attributeType() == QAttribute::IndexAttribute) { + if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) + m_submissionContext->specifyIndices(buffer); + // Vertex Attribute + } else if (command->m_attributes.contains(attribute->nameId())) { + if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) { + // Find the location for the attribute + const QVector<ShaderAttribute> shaderAttributes = shader->attributes(); + const ShaderAttribute *attributeDescription = nullptr; + for (const ShaderAttribute &shaderAttribute : shaderAttributes) { + if (shaderAttribute.m_nameId == attribute->nameId()) { + attributeDescription = &shaderAttribute; + break; + } + } + if (!attributeDescription || attributeDescription->m_location < 0) + return false; + m_submissionContext->specifyAttribute(attribute, buffer, attributeDescription); + } + } + + // Append attribute to temporary vector so that its dirtiness + // can be cleared at the end of the frame + if (attributeWasDirty) + m_dirtyAttributes.push_back(attribute); + + // Note: We cannot call unsertDirty on the Attribute at this + // point as we don't know if the attributes are being shared + // with other geometry / geometryRenderer in which case they still + // should remain dirty so that VAO for these commands are properly + // updated + } + + return true; +} + +bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry, + RenderCommand *command) const +{ + const auto attributeIds = geometry->attributes(); + + for (QNodeId attributeId : attributeIds) { + // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager + Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + + if (attribute == nullptr) + continue; + + if ((attribute->attributeType() == QAttribute::IndexAttribute && attribute->isDirty()) || + (command->m_attributes.contains(attribute->nameId()) && attribute->isDirty())) + return true; + } + return false; +} + +// Erase graphics related resources that may become unused after a frame +void Renderer::cleanGraphicsResources() +{ + // Clean buffers + const QVector<Qt3DCore::QNodeId> buffersToRelease = m_nodesManager->bufferManager()->takeBuffersToRelease(); + for (Qt3DCore::QNodeId bufferId : buffersToRelease) + m_submissionContext->releaseBuffer(bufferId); + + // Delete abandoned textures + const QVector<GLTexture*> abandonedTextures = m_nodesManager->glTextureManager()->takeAbandonedTextures(); + for (GLTexture *tex : abandonedTextures) { + tex->destroyGLTexture(); + delete tex; + } + + // Delete abandoned VAOs + m_abandonedVaosMutex.lock(); + const QVector<HVao> abandonedVaos = std::move(m_abandonedVaos); + m_abandonedVaosMutex.unlock(); + for (const HVao &vaoHandle : abandonedVaos) { + // might have already been destroyed last frame, but added by the cleanup job before, so + // check if the VAO is really still existent + OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle); + if (vao) { + vao->destroy(); + m_nodesManager->vaoManager()->release(vaoHandle); + } + } +} + +QList<QPair<QObject *, QMouseEvent>> Renderer::pendingPickingEvents() const +{ + return m_pickEventFilter->pendingMouseEvents(); +} + +QList<QKeyEvent> Renderer::pendingKeyEvents() const +{ + return m_pickEventFilter->pendingKeyEvents(); +} + +const GraphicsApiFilterData *Renderer::contextInfo() const +{ + return m_submissionContext->contextInfo(); +} + +SubmissionContext *Renderer::submissionContext() const +{ + return m_submissionContext.data(); +} + +void Renderer::addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId) +{ + if (!m_pendingRenderCaptureSendRequests.contains(nodeId)) + m_pendingRenderCaptureSendRequests.push_back(nodeId); +} + +const QVector<Qt3DCore::QNodeId> Renderer::takePendingRenderCaptureSendRequests() +{ + return std::move(m_pendingRenderCaptureSendRequests); +} + +// Returns a vector of jobs to be performed for dirty buffers +// 1 dirty buffer == 1 job, all job can be performed in parallel +QVector<Qt3DCore::QAspectJobPtr> Renderer::createRenderBufferJobs() const +{ + const QVector<QNodeId> dirtyBuffers = m_nodesManager->bufferManager()->takeDirtyBuffers(); + QVector<QAspectJobPtr> dirtyBuffersJobs; + dirtyBuffersJobs.reserve(dirtyBuffers.size()); + + for (const QNodeId bufId : dirtyBuffers) { + Render::HBuffer bufferHandle = m_nodesManager->lookupHandle<Render::Buffer, Render::BufferManager, Render::HBuffer>(bufId); + if (!bufferHandle.isNull()) { + // Create new buffer job + auto job = Render::LoadBufferJobPtr::create(bufferHandle); + job->setNodeManager(m_nodesManager); + dirtyBuffersJobs.push_back(job); + } + } + + return dirtyBuffersJobs; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/renderer.pri b/src/render/renderers/opengl/renderer/renderer.pri new file mode 100644 index 000000000..34f6064bd --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderer.pri @@ -0,0 +1,27 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/commandthread.cpp \ + $$PWD/glcommands.cpp \ + $$PWD/openglvertexarrayobject.cpp \ + $$PWD/rendercommand.cpp \ + $$PWD/renderer.cpp \ + $$PWD/renderqueue.cpp \ + $$PWD/renderview.cpp \ + $$PWD/renderviewbuilder.cpp \ + $$PWD/shaderparameterpack.cpp + +HEADERS += \ + $$PWD/commandthread_p.h \ + $$PWD/glcommands_p.h \ + $$PWD/openglvertexarrayobject_p.h \ + $$PWD/renderercache_p.h \ + $$PWD/rendercommand_p.h \ + $$PWD/renderer_p.h \ + $$PWD/renderqueue_p.h \ + $$PWD/renderview_p.h \ + $$PWD/renderviewbuilder_p.h \ + $$PWD/shaderparameterpack_p.h \ + $$PWD/shadervariables_p.h + + diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h new file mode 100644 index 000000000..b4ad0b0fe --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderer_p.h @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERER_H +#define QT3DRENDER_RENDER_RENDERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DRender/qtechnique.h> +#include <Qt3DRender/private/shaderparameterpack_p.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/abstractrenderer_p.h> +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/qt3drender_global_p.h> +#include <Qt3DRender/private/pickboundingvolumejob_p.h> +#include <Qt3DRender/private/raycastingjob_p.h> +#include <Qt3DRender/private/rendersettings_p.h> +#include <Qt3DRender/private/renderviewinitializerjob_p.h> +#include <Qt3DRender/private/expandboundingvolumejob_p.h> +#include <Qt3DRender/private/updateworldtransformjob_p.h> +#include <Qt3DRender/private/calcboundingvolumejob_p.h> +#include <Qt3DRender/private/updateshaderdatatransformjob_p.h> +#include <Qt3DRender/private/framecleanupjob_p.h> +#include <Qt3DRender/private/updateworldboundingvolumejob_p.h> +#include <Qt3DRender/private/updatetreeenabledjob_p.h> +#include <Qt3DRender/private/platformsurfacefilter_p.h> +#include <Qt3DRender/private/sendrendercapturejob_p.h> +#include <Qt3DRender/private/sendbuffercapturejob_p.h> +#include <Qt3DRender/private/genericlambdajob_p.h> +#include <Qt3DRender/private/updatemeshtrianglelistjob_p.h> +#include <Qt3DRender/private/filtercompatibletechniquejob_p.h> +#include <Qt3DRender/private/updateskinningpalettejob_p.h> +#include <Qt3DRender/private/renderercache_p.h> + +#include <QHash> +#include <QMatrix4x4> +#include <QObject> + +#include <QOpenGLShaderProgram> +#include <QOpenGLVertexArrayObject> +#include <QOpenGLBuffer> +#include <QMutex> +#include <QWaitCondition> +#include <QAtomicInt> +#include <QScopedPointer> +#include <QSemaphore> + +#include <functional> + +QT_BEGIN_NAMESPACE + +class QSurface; +class QMouseEvent; + +namespace Qt3DCore { +class QEntity; +class QFrameAllocator; +class QEventFilterService; +} + +namespace Qt3DRender { + +class QCamera; +class QMaterial; +class QShaderProgram; +class QMesh; +class QRenderPass; +class QAbstractShapeMesh; +struct GraphicsApiFilterData; +class QSceneImporter; + +#if QT_CONFIG(qt3d_profile_jobs) +namespace Debug { +class CommandExecuter; +} +#endif + +namespace Render { + +class CameraLens; +class SubmissionContext; +class FrameGraphNode; +class Material; +class Technique; +class Shader; +class Entity; +class RenderCommand; +class RenderQueue; +class RenderView; +class Effect; +class RenderPass; +class RenderThread; +class CommandThread; +class RenderStateSet; +class VSyncFrameAdvanceService; +class PickEventFilter; +class NodeManagers; +class ShaderCache; + +class UpdateLevelOfDetailJob; +typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr; + +using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; +using IntrospectShadersJobPtr = GenericLambdaJobPtr<std::function<void()>>; + +class QT3DRENDERSHARED_PRIVATE_EXPORT Renderer : public AbstractRenderer +{ +public: + explicit Renderer(QRenderAspect::RenderType type); + ~Renderer(); + + void dumpInfo() const override; + API api() const override { return AbstractRenderer::OpenGL; } + + qint64 time() const override; + void setTime(qint64 time) override; + + void setNodeManagers(NodeManagers *managers) override; + void setServices(Qt3DCore::QServiceLocator *services) override; + void setSurfaceExposed(bool exposed) override; + + NodeManagers *nodeManagers() const override; + Qt3DCore::QServiceLocator *services() const override { return m_services; } + + void initialize() override; + void shutdown() override; + void releaseGraphicsResources() override; + + void render() override; + void doRender(bool scene3dBlocking = false) override; + void cleanGraphicsResources() override; + + bool isRunning() const override { return m_running.load(); } + + void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Entity *sgRoot) override; + Entity *sceneRoot() const override { return m_renderSceneRoot; } + + FrameGraphNode *frameGraphRoot() const override; + + void markDirty(BackendNodeDirtySet changes, BackendNode *node) override; + BackendNodeDirtySet dirtyBits() override; + +#if defined(QT_BUILD_INTERNAL) + void clearDirtyBits(BackendNodeDirtySet changes) override; +#endif + bool shouldRender() override; + void skipNextFrame() override; + + QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override; + Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override; + Qt3DCore::QAspectJobPtr rayCastingJob() override; + Qt3DCore::QAspectJobPtr syncTextureLoadingJob() override; + Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() override; + + QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const; + + inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; } + inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; } + inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; } + inline UpdateTreeEnabledJobPtr updateTreeEnabledJob() const { return m_updateTreeEnabledJob; } + inline UpdateWorldTransformJobPtr updateWorldTransformJob() const { return m_worldTransformJob; } + inline UpdateWorldBoundingVolumeJobPtr updateWorldBoundingVolumeJob() const { return m_updateWorldBoundingVolumeJob; } + inline UpdateLevelOfDetailJobPtr updateLevelOfDetailJob() const { return m_updateLevelOfDetailJob; } + inline UpdateMeshTriangleListJobPtr updateMeshTriangleListJob() const { return m_updateMeshTriangleListJob; } + inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const { return m_filterCompatibleTechniqueJob; } + inline SynchronizerJobPtr textureLoadSyncJob() const { return m_syncTextureLoadingJob; } + inline UpdateSkinningPaletteJobPtr updateSkinningPaletteJob() const { return m_updateSkinningPaletteJob; } + inline IntrospectShadersJobPtr introspectShadersJob() const { return m_introspectShaderJob; } + inline Qt3DCore::QAspectJobPtr bufferGathererJob() const { return m_bufferGathererJob; } + inline Qt3DCore::QAspectJobPtr textureGathererJob() const { return m_textureGathererJob; } + + Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override; + + void registerEventFilter(Qt3DCore::QEventFilterService *service) override; + + void setSettings(RenderSettings *settings) override; + RenderSettings *settings() const override; + QOpenGLContext *shareContext() const override; + + + // Executed in secondary GL thread + void loadShader(Shader *shader) const override; + + + void updateGLResources(); + void updateTexture(Texture *texture); + void cleanupTexture(const Texture *texture); + void downloadGLBuffers(); + void blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, + Qt3DCore::QNodeId outputRenderTargetId, + QRect inputRect, + QRect outputRect, + GLuint defaultFramebuffer); + + void prepareCommandsSubmission(const QVector<RenderView *> &renderViews); + bool executeCommandsSubmission(const RenderView *rv); + bool updateVAOWithAttributes(Geometry *geometry, + RenderCommand *command, + Shader *shader, + bool forceUpdate); + + bool requiresVAOAttributeUpdate(Geometry *geometry, + RenderCommand *command) const; + + void setOpenGLContext(QOpenGLContext *context); + const GraphicsApiFilterData *contextInfo() const; + SubmissionContext *submissionContext() const; + + inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; } + + QList<QPair<QObject*, QMouseEvent>> pendingPickingEvents() const; + QList<QKeyEvent> pendingKeyEvents() const; + + void addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId); + const QVector<Qt3DCore::QNodeId> takePendingRenderCaptureSendRequests(); + + void enqueueRenderView(RenderView *renderView, int submitOrder); + bool isReadyToSubmit(); + + QVariant executeCommand(const QStringList &args) override; + void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override; + QSurfaceFormat format() override; + + struct ViewSubmissionResultData + { + ViewSubmissionResultData() + : lastBoundFBOId(0) + , surface(nullptr) + {} + + uint lastBoundFBOId; + QSurface *surface; + }; + + ViewSubmissionResultData submitRenderViews(const QVector<Render::RenderView *> &renderViews); + + RendererCache *cache() { return &m_cache; } + +#ifdef QT3D_RENDER_UNIT_TESTS +public: +#else + +private: +#endif + bool canRender() const; + + Qt3DCore::QServiceLocator *m_services; + NodeManagers *m_nodesManager; + + // Frame graph root + Qt3DCore::QNodeId m_frameGraphRootUuid; + + Entity *m_renderSceneRoot; + + // Fail safe values that we can use if a RenderCommand + // is missing a shader + RenderStateSet *m_defaultRenderStateSet; + ShaderParameterPack m_defaultUniformPack; + + QScopedPointer<SubmissionContext> m_submissionContext; + QSurfaceFormat m_format; + + RenderQueue *m_renderQueue; + QScopedPointer<RenderThread> m_renderThread; + QScopedPointer<CommandThread> m_commandThread; + QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService; + + QSemaphore m_submitRenderViewsSemaphore; + QSemaphore m_waitForInitializationToBeCompleted; + + QAtomicInt m_running; + + QScopedPointer<PickEventFilter> m_pickEventFilter; + + QVector<Attribute *> m_dirtyAttributes; + QVector<Geometry *> m_dirtyGeometry; + QAtomicInt m_exposed; + + struct DirtyBits { + BackendNodeDirtySet marked = 0; // marked dirty since last job build + BackendNodeDirtySet remaining = 0; // remaining dirty after jobs have finished + }; + DirtyBits m_dirtyBits; + + QAtomicInt m_lastFrameCorrect; + QOpenGLContext *m_glContext; + QOpenGLContext *m_shareContext; + mutable QMutex m_shareContextMutex; + ShaderCache *m_shaderCache; + PickBoundingVolumeJobPtr m_pickBoundingVolumeJob; + RayCastingJobPtr m_rayCastingJob; + + qint64 m_time; + + RenderSettings *m_settings; + + UpdateShaderDataTransformJobPtr m_updateShaderDataTransformJob; + FrameCleanupJobPtr m_cleanupJob; + UpdateWorldTransformJobPtr m_worldTransformJob; + ExpandBoundingVolumeJobPtr m_expandBoundingVolumeJob; + CalculateBoundingVolumeJobPtr m_calculateBoundingVolumeJob; + UpdateWorldBoundingVolumeJobPtr m_updateWorldBoundingVolumeJob; + UpdateTreeEnabledJobPtr m_updateTreeEnabledJob; + SendRenderCaptureJobPtr m_sendRenderCaptureJob; + SendBufferCaptureJobPtr m_sendBufferCaptureJob; + UpdateSkinningPaletteJobPtr m_updateSkinningPaletteJob; + UpdateLevelOfDetailJobPtr m_updateLevelOfDetailJob; + UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob; + FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob; + + QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests; + + void performDraw(RenderCommand *command); + void performCompute(const RenderView *rv, RenderCommand *command); + void createOrUpdateVAO(RenderCommand *command, + HVao *previousVAOHandle, + OpenGLVertexArrayObject **vao); + + GenericLambdaJobPtr<std::function<void ()>> m_bufferGathererJob; + GenericLambdaJobPtr<std::function<void ()>> m_vaoGathererJob; + GenericLambdaJobPtr<std::function<void ()>> m_textureGathererJob; + IntrospectShadersJobPtr m_introspectShaderJob; + + SynchronizerJobPtr m_syncTextureLoadingJob; + + void lookForAbandonedVaos(); + void lookForDirtyBuffers(); + void lookForDownloadableBuffers(); + void lookForDirtyTextures(); + void reloadDirtyShaders(); + + QMutex m_abandonedVaosMutex; + QVector<HVao> m_abandonedVaos; + + QVector<HBuffer> m_dirtyBuffers; + QVector<HBuffer> m_downloadableBuffers; + QVector<HTexture> m_dirtyTextures; + + bool m_ownedContext; + + OffscreenSurfaceHelper *m_offscreenHelper; + QMutex m_offscreenSurfaceMutex; + +#if QT_CONFIG(qt3d_profile_jobs) + QScopedPointer<Qt3DRender::Debug::CommandExecuter> m_commandExecuter; + friend class Qt3DRender::Debug::CommandExecuter; +#endif + + QMetaObject::Connection m_contextConnection; + RendererCache m_cache; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERER_H diff --git a/src/render/renderers/opengl/renderer/renderercache_p.h b/src/render/renderers/opengl/renderer/renderercache_p.h new file mode 100644 index 000000000..2aa50d131 --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderercache_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERERCACHE_P_H +#define QT3DRENDER_RENDER_RENDERERCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/QFrameGraphNode> + +#include <Qt3DRender/private/entity_p.h> +#include <Qt3DRender/private/renderviewjobutils_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +struct RendererCache +{ + struct LeafNodeData + { + QVector<Entity *> filterEntitiesByLayer; + MaterialParameterGathererData materialParameterGatherer; + }; + + QHash<FrameGraphNode *, LeafNodeData> leafNodeCache; + + QMutex *mutex() { return &m_mutex; } + +private: + QMutex m_mutex; +}; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERERCACHE_P_H diff --git a/src/render/renderers/opengl/renderer/renderqueue.cpp b/src/render/renderers/opengl/renderer/renderqueue.cpp new file mode 100644 index 000000000..bd9d3ee59 --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderqueue.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderqueue_p.h" +#include <Qt3DRender/private/renderview_p.h> +#include <QThread> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +RenderQueue::RenderQueue() + : m_noRender(false) + , m_wasReset(true) + , m_targetRenderViewCount(0) + , m_currentRenderViewCount(0) + , m_currentWorkQueue(1) +{ +} + +int RenderQueue::currentRenderViewCount() const +{ + return m_currentRenderViewCount; +} + +/* + * In case the framegraph changed or when the current number of render queue + * needs to be reset. + */ +void RenderQueue::reset() +{ + m_currentRenderViewCount = 0; + m_targetRenderViewCount = 0; + m_currentWorkQueue.clear(); + m_noRender = false; + m_wasReset = true; +} + +void RenderQueue::setNoRender() +{ + Q_ASSERT(m_targetRenderViewCount == 0); + m_noRender = true; +} + +/* + * Queue up a RenderView for the frame being built. + * Thread safe as this is called from the renderer which is locked. + * Returns true if the renderView is complete + */ +bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIndex) +{ + Q_ASSERT(!m_noRender); + m_currentWorkQueue[submissionOrderIndex] = renderView; + ++m_currentRenderViewCount; + Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount); + return isFrameQueueComplete(); +} + +/* + * Called by the Rendering Thread to retrieve the a frame queue to render. + * A call to reset is required after rendering of the frame. Otherwise under some + * conditions the current but then invalidated frame queue could be reused. + */ +QVector<RenderView *> RenderQueue::nextFrameQueue() +{ + return m_currentWorkQueue; +} + +/* + * Sets the number \a targetRenderViewCount of RenderView objects that make up a frame. + */ +void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount) +{ + Q_ASSERT(!m_noRender); + m_targetRenderViewCount = targetRenderViewCount; + m_currentWorkQueue.resize(targetRenderViewCount); + m_wasReset = false; +} + +/* + * Returns true if all the RenderView objects making up the current frame have been queued. + * Returns false otherwise. + * \note a frameQueue or size 0 is considered incomplete. + */ +bool RenderQueue::isFrameQueueComplete() const +{ + return (m_noRender + || (m_targetRenderViewCount > 0 && m_targetRenderViewCount == m_currentRenderViewCount)); +} + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/renderqueue_p.h b/src/render/renderers/opengl/renderer/renderqueue_p.h new file mode 100644 index 000000000..e565115f2 --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderqueue_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERQUEUE_H +#define QT3DRENDER_RENDER_RENDERQUEUE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QVector> +#include <QtGlobal> +#include <QMutex> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class RenderView; + +class Q_AUTOTEST_EXPORT RenderQueue +{ +public: + RenderQueue(); + + void setTargetRenderViewCount(int targetRenderViewCount); + int targetRenderViewCount() const { return m_targetRenderViewCount; } + int currentRenderViewCount() const; + bool isFrameQueueComplete() const; + + bool queueRenderView(RenderView *renderView, uint submissionOrderIndex); + QVector<RenderView *> nextFrameQueue(); + void reset(); + + void setNoRender(); + inline bool isNoRender() const { return m_noRender; } + + inline bool wasReset() const { return m_wasReset; } + + inline QMutex *mutex() { return &m_mutex; } + +private: + bool m_noRender; + bool m_wasReset; + int m_targetRenderViewCount; + int m_currentRenderViewCount; + QVector<RenderView *> m_currentWorkQueue; + QMutex m_mutex; +}; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERQUEUE_H diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp new file mode 100644 index 000000000..c29448570 --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderview.cpp @@ -0,0 +1,1108 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderview_p.h" +#include <Qt3DRender/qmaterial.h> +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DRender/qrendertarget.h> +#include <Qt3DRender/qabstractlight.h> +#include <Qt3DRender/private/sphere_p.h> + +#include <Qt3DRender/private/cameraselectornode_p.h> +#include <Qt3DRender/private/framegraphnode_p.h> +#include <Qt3DRender/private/layerfilternode_p.h> +#include <Qt3DRender/private/qparameter_p.h> +#include <Qt3DRender/private/cameralens_p.h> +#include <Qt3DRender/private/rendercommand_p.h> +#include <Qt3DRender/private/effect_p.h> +#include <Qt3DRender/private/entity_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/layer_p.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/renderpassfilternode_p.h> +#include <Qt3DRender/private/renderpass_p.h> +#include <Qt3DRender/private/geometryrenderer_p.h> +#include <Qt3DRender/private/renderstateset_p.h> +#include <Qt3DRender/private/techniquefilternode_p.h> +#include <Qt3DRender/private/viewportnode_p.h> +#include <Qt3DRender/private/buffermanager_p.h> +#include <Qt3DRender/private/geometryrenderermanager_p.h> +#include <Qt3DRender/private/rendercapture_p.h> +#include <Qt3DRender/private/buffercapture_p.h> +#include <Qt3DRender/private/stringtoint_p.h> +#include <Qt3DCore/qentity.h> +#include <QtGui/qsurface.h> +#include <algorithm> + +#include <QDebug> +#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) +#include <QElapsedTimer> +#endif + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + + +namespace { + +// register our QNodeId's as a metatype during program loading +const int Q_DECL_UNUSED qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>(); + +const int MAX_LIGHTS = 8; + +#define LIGHT_POSITION_NAME QLatin1String(".position") +#define LIGHT_TYPE_NAME QLatin1String(".type") +#define LIGHT_COLOR_NAME QLatin1String(".color") +#define LIGHT_INTENSITY_NAME QLatin1String(".intensity") + +int LIGHT_COUNT_NAME_ID = 0; +int LIGHT_POSITION_NAMES[MAX_LIGHTS]; +int LIGHT_TYPE_NAMES[MAX_LIGHTS]; +int LIGHT_COLOR_NAMES[MAX_LIGHTS]; +int LIGHT_INTENSITY_NAMES[MAX_LIGHTS]; +QString LIGHT_STRUCT_NAMES[MAX_LIGHTS]; + +} // anonymous namespace + +bool wasInitialized = false; +RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters; + + +RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniformSetters() +{ + RenderView::StandardUniformsNameToTypeHash setters; + + setters.insert(StringToInt::lookupId(QLatin1String("modelMatrix")), ModelMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("viewMatrix")), ViewMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("projectionMatrix")), ProjectionMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("modelView")), ModelViewMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("viewProjectionMatrix")), ViewProjectionMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("modelViewProjection")), ModelViewProjectionMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("mvp")), ModelViewProjectionMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("inverseModelMatrix")), InverseModelMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("inverseViewMatrix")), InverseViewMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("inverseProjectionMatrix")), InverseProjectionMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("inverseModelView")), InverseModelViewMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("inverseViewProjectionMatrix")), InverseViewProjectionMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("inverseModelViewProjection")), InverseModelViewProjectionMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("modelNormalMatrix")), ModelNormalMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("modelViewNormal")), ModelViewNormalMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("viewportMatrix")), ViewportMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("inverseViewportMatrix")), InverseViewportMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("aspectRatio")), AspectRatio); + setters.insert(StringToInt::lookupId(QLatin1String("exposure")), Exposure); + setters.insert(StringToInt::lookupId(QLatin1String("gamma")), Gamma); + setters.insert(StringToInt::lookupId(QLatin1String("time")), Time); + setters.insert(StringToInt::lookupId(QLatin1String("eyePosition")), EyePosition); + setters.insert(StringToInt::lookupId(QLatin1String("skinningPalette[0]")), SkinningPalette); + + return setters; +} + +// TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too +static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize) +{ + return QRectF(fractionalViewport.x() * surfaceSize.width(), + (1.0 - fractionalViewport.y() - fractionalViewport.height()) * surfaceSize.height(), + fractionalViewport.width() * surfaceSize.width(), + fractionalViewport.height() * surfaceSize.height()); +} + +static Matrix4x4 getProjectionMatrix(const CameraLens *lens) +{ + if (!lens) + qWarning() << "[Qt3D Renderer] No Camera Lens found. Add a CameraSelector to your Frame Graph or make sure that no entities will be rendered."; + return lens ? lens->projection() : Matrix4x4(); +} + +UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType, + Entity *entity, + const Matrix4x4 &model) const +{ + switch (standardUniformType) { + case ModelMatrix: + return UniformValue(model); + case ViewMatrix: + return UniformValue(m_data.m_viewMatrix); + case ProjectionMatrix: + return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens)); + case ModelViewMatrix: + return UniformValue(m_data.m_viewMatrix * model); + case ViewProjectionMatrix: + return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix); + case ModelViewProjectionMatrix: + return UniformValue(m_data.m_viewProjectionMatrix * model); + case InverseModelMatrix: + return UniformValue(model.inverted()); + case InverseViewMatrix: + return UniformValue(m_data.m_viewMatrix.inverted()); + case InverseProjectionMatrix: { + return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens).inverted()); + } + case InverseModelViewMatrix: + return UniformValue((m_data.m_viewMatrix * model).inverted()); + case InverseViewProjectionMatrix: { + const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix; + return UniformValue(viewProjectionMatrix.inverted()); + } + case InverseModelViewProjectionMatrix: + return UniformValue((m_data.m_viewProjectionMatrix * model).inverted()); + case ModelNormalMatrix: + return UniformValue(convertToQMatrix4x4(model).normalMatrix()); + case ModelViewNormalMatrix: + return UniformValue(convertToQMatrix4x4(m_data.m_viewMatrix * model).normalMatrix()); + case ViewportMatrix: { + QMatrix4x4 viewportMatrix; + // TO DO: Implement on Matrix4x4 + viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); + return UniformValue(Matrix4x4(viewportMatrix)); + } + case InverseViewportMatrix: { + QMatrix4x4 viewportMatrix; + // TO DO: Implement on Matrix4x4 + viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); + return UniformValue(Matrix4x4(viewportMatrix.inverted())); + } + case AspectRatio: + return float(m_surfaceSize.width()) / float(m_surfaceSize.height()); + case Exposure: + return UniformValue(m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f); + case Gamma: + return UniformValue(m_gamma); + case Time: + return UniformValue(float(m_renderer->time() / 1000000000.0f)); + case EyePosition: + return UniformValue(m_data.m_eyePos); + case SkinningPalette: { + const Armature *armature = entity->renderComponent<Armature>(); + if (!armature) { + qCWarning(Jobs, "Requesting skinningPalette uniform but no armature set on entity"); + return UniformValue(); + } + return armature->skinningPaletteUniform(); + } + default: + Q_UNREACHABLE(); + return UniformValue(); + } +} + +RenderView::RenderView() + : m_isDownloadBuffersEnable(false) + , m_hasBlitFramebufferInfo(false) + , m_renderer(nullptr) + , m_devicePixelRatio(1.) + , m_viewport(QRectF(0.0f, 0.0f, 1.0f, 1.0f)) + , m_gamma(2.2f) + , m_surface(nullptr) + , m_clearBuffer(QClearBuffers::None) + , m_stateSet(nullptr) + , m_noDraw(false) + , m_compute(false) + , m_frustumCulling(false) + , m_memoryBarrier(QMemoryBarrier::None) + , m_environmentLight(nullptr) +{ + m_workGroups[0] = 1; + m_workGroups[1] = 1; + m_workGroups[2] = 1; + + if (Q_UNLIKELY(!wasInitialized)) { + // Needed as we can control the init order of static/global variables across compile units + // and this hash relies on the static StringToInt class + wasInitialized = true; + RenderView::ms_standardUniformSetters = RenderView::initializeStandardUniformSetters(); + LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount")); + for (int i = 0; i < MAX_LIGHTS; ++i) { + Q_STATIC_ASSERT_X(MAX_LIGHTS < 10, "can't use the QChar trick anymore"); + LIGHT_STRUCT_NAMES[i] = QLatin1String("lights[") + QLatin1Char(char('0' + i)) + QLatin1Char(']'); + LIGHT_POSITION_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_POSITION_NAME); + LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME); + LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME); + LIGHT_INTENSITY_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME); + } + } +} + +RenderView::~RenderView() +{ + delete m_stateSet; + for (RenderCommand *command : qAsConst(m_commands)) { + delete command->m_stateSet; + delete command; + } +} + +namespace { + +template<int SortType> +struct AdjacentSubRangeFinder +{ + static bool adjacentSubRange(RenderCommand *, RenderCommand *) + { + Q_UNREACHABLE(); + return false; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost> +{ + static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + { + return a->m_changeCost == b->m_changeCost; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::BackToFront> +{ + static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + { + return a->m_depth == b->m_depth; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::Material> +{ + static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + { + return a->m_shaderDna == b->m_shaderDna; + } +}; + +template<> +struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack> +{ + static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + { + return a->m_depth == b->m_depth; + } +}; + +template<typename Predicate> +int advanceUntilNonAdjacent(const QVector<RenderCommand *> &commands, + const int beg, const int end, Predicate pred) +{ + int i = beg + 1; + while (i < end) { + if (!pred(*(commands.begin() + beg), *(commands.begin() + i))) + break; + ++i; + } + return i; +} + + +using CommandIt = QVector<RenderCommand *>::iterator; + +template<int SortType> +struct SubRangeSorter +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + Q_UNUSED(begin); + Q_UNUSED(end); + Q_UNREACHABLE(); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::StateChangeCost> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { + return a->m_changeCost > b->m_changeCost; + }); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::BackToFront> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { + return a->m_depth > b->m_depth; + }); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::Material> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + // First we sort by shaderDNA + std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { + return a->m_shaderDna > b->m_shaderDna; + }); + } +}; + +template<> +struct SubRangeSorter<QSortPolicy::FrontToBack> +{ + static void sortSubRange(CommandIt begin, const CommandIt end) + { + std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { + return a->m_depth < b->m_depth; + }); + } +}; + +int findSubRange(const QVector<RenderCommand *> &commands, + const int begin, const int end, + const QSortPolicy::SortType sortType) +{ + switch (sortType) { + case QSortPolicy::StateChangeCost: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange); + case QSortPolicy::BackToFront: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange); + case QSortPolicy::Material: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + case QSortPolicy::FrontToBack: + return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange); + default: + Q_UNREACHABLE(); + return end; + } +} + +void sortByMaterial(QVector<RenderCommand *> &commands, int begin, const int end) +{ + // We try to arrange elements so that their rendering cost is minimized for a given shader + int rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + while (begin != end) { + if (begin + 1 < rangeEnd) { + std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (RenderCommand *a, RenderCommand *b){ + return a->m_material.handle() < b->m_material.handle(); + }); + } + begin = rangeEnd; + rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); + } +} + +void sortCommandRange(QVector<RenderCommand *> &commands, int begin, const int end, const int level, + const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes) +{ + if (level >= sortingTypes.size()) + return; + + switch (sortingTypes.at(level)) { + case QSortPolicy::StateChangeCost: + SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(commands.begin() + begin, commands.begin() + end); + break; + case QSortPolicy::BackToFront: + SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(commands.begin() + begin, commands.begin() + end); + break; + case QSortPolicy::Material: + // Groups all same shader DNA together + SubRangeSorter<QSortPolicy::Material>::sortSubRange(commands.begin() + begin, commands.begin() + end); + // Group all same material together (same parameters most likely) + sortByMaterial(commands, begin, end); + break; + case QSortPolicy::FrontToBack: + SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(commands.begin() + begin, commands.begin() + end); + break; + default: + Q_UNREACHABLE(); + } + + // For all sub ranges of adjacent item for sortType[i] + // Perform filtering with sortType[i + 1] + int rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level)); + while (begin != end) { + sortCommandRange(commands, begin, rangeEnd, level + 1, sortingTypes); + begin = rangeEnd; + rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level)); + } +} + +} // anonymous + +void RenderView::sort() +{ + sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes); + + // For RenderCommand with the same shader + // We compute the adjacent change cost + + // Minimize uniform changes + int i = 0; + while (i < m_commands.size()) { + int j = i; + + // Advance while commands share the same shader + while (i < m_commands.size() && m_commands[j]->m_shaderDna == m_commands[i]->m_shaderDna) + ++i; + + if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes + PackUniformHash cachedUniforms = m_commands[j++]->m_parameterPack.uniforms(); + + while (j < i) { + // We need the reference here as we are modifying the original container + // not the copy + PackUniformHash &uniforms = m_commands.at(j)->m_parameterPack.m_uniforms; + PackUniformHash::iterator it = uniforms.begin(); + const PackUniformHash::iterator end = uniforms.end(); + + while (it != end) { + // We are comparing the values: + // - raw uniform values + // - the texture Node id if the uniform represents a texture + // since all textures are assigned texture units before the RenderCommands + // sharing the same material (shader) are rendered, we can't have the case + // where two uniforms, referencing the same texture eventually have 2 different + // texture unit values + const UniformValue refValue = cachedUniforms.value(it.key()); + if (it.value() == refValue) { + it = uniforms.erase(it); + } else { + cachedUniforms.insert(it.key(), it.value()); + ++it; + } + } + ++j; + } + } + } +} + +void RenderView::setRenderer(Renderer *renderer) +{ + m_renderer = renderer; + m_manager = renderer->nodeManagers(); +} + +void RenderView::addClearBuffers(const ClearBuffers *cb) { + QClearBuffers::BufferTypeFlags type = cb->type(); + + if (type & QClearBuffers::StencilBuffer) { + m_clearStencilValue = cb->clearStencilValue(); + m_clearBuffer |= QClearBuffers::StencilBuffer; + } + if (type & QClearBuffers::DepthBuffer) { + m_clearDepthValue = cb->clearDepthValue(); + m_clearBuffer |= QClearBuffers::DepthBuffer; + } + // keep track of global ClearColor (if set) and collect all DrawBuffer-specific + // ClearColors + if (type & QClearBuffers::ColorBuffer) { + ClearBufferInfo clearBufferInfo; + clearBufferInfo.clearColor = cb->clearColor(); + + if (cb->clearsAllColorBuffers()) { + m_globalClearColorBuffer = clearBufferInfo; + m_clearBuffer |= QClearBuffers::ColorBuffer; + } else { + if (cb->bufferId()) { + const RenderTargetOutput *targetOutput = m_manager->attachmentManager()->lookupResource(cb->bufferId()); + if (targetOutput) { + clearBufferInfo.attchmentPoint = targetOutput->point(); + // Note: a job is later performed to find the drawIndex from the buffer attachment point + // using the AttachmentPack + m_specificClearColorBuffers.push_back(clearBufferInfo); + } + } + } + } +} + +// If we are there, we know that entity had a GeometryRenderer + Material +QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities) const +{ + // Note: since many threads can be building render commands + // we need to ensure that the UniformBlockValueBuilder they are using + // is only accessed from the same thread + UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); + builder->shaderDataManager = m_manager->shaderDataManager(); + builder->textureManager = m_manager->textureManager(); + m_localData.setLocalData(builder); + + QVector<RenderCommand *> commands; + commands.reserve(entities.size()); + + for (Entity *entity : entities) { + GeometryRenderer *geometryRenderer = nullptr; + HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>(); + + // There is a geometry renderer with geometry + if ((geometryRenderer = m_manager->geometryRendererManager()->data(geometryRendererHandle)) != nullptr + && geometryRenderer->isEnabled() + && !geometryRenderer->geometryId().isNull()) { + + const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); + const HMaterial materialHandle = entity->componentHandle<Material>(); + const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId); + HGeometry geometryHandle = m_manager->lookupHandle<Geometry, GeometryManager, HGeometry>(geometryRenderer->geometryId()); + Geometry *geometry = m_manager->data<Geometry, GeometryManager>(geometryHandle); + + // 1 RenderCommand per RenderPass pass on an Entity with a Mesh + for (const RenderPassParameterData &passData : renderPassData) { + // Add the RenderPass Parameters + RenderCommand *command = new RenderCommand(); + + // Project the camera-to-object-center vector onto the camera + // view vector. This gives a depth value suitable as the key + // for BackToFront sorting. + command->m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); + + command->m_geometry = geometryHandle; + command->m_geometryRenderer = geometryRendererHandle; + command->m_material = materialHandle; + // For RenderPass based states we use the globally set RenderState + // if no renderstates are defined as part of the pass. That means: + // RenderPass { renderStates: [] } will use the states defined by + // StateSet in the FrameGraph + RenderPass *pass = passData.pass; + if (pass->hasRenderStates()) { + command->m_stateSet = new RenderStateSet(); + addToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager()); + + // Merge per pass stateset with global stateset + // so that the local stateset only overrides + if (m_stateSet != nullptr) + command->m_stateSet->merge(m_stateSet); + command->m_changeCost = m_renderer->defaultRenderState()->changeCost(command->m_stateSet); + } + + // Pick which lights to take in to account. + // For now decide based on the distance by taking the MAX_LIGHTS closest lights. + // Replace with more sophisticated mechanisms later. + // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command + QVector<LightSource> lightSources = m_lightSources; + if (lightSources.size() > 1) { + const Vector3D entityCenter = entity->worldBoundingVolume()->center(); + std::sort(lightSources.begin(), lightSources.end(), + [&] (const LightSource &a, const LightSource &b) { + const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center()); + const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center()); + return distA < distB; + }); + } + + ParameterInfoList globalParameters = passData.parameterInfo; + // setShaderAndUniforms can initialize a localData + // make sure this is cleared before we leave this function + setShaderAndUniforms(command, + pass, + globalParameters, + entity, + lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)), + m_environmentLight); + + // Store all necessary information for actual drawing if command is valid + command->m_isValid = !command->m_attributes.empty(); + if (command->m_isValid) { + // Update the draw command with what's going to be needed for the drawing + uint primitiveCount = geometryRenderer->vertexCount(); + uint estimatedCount = 0; + Attribute *indexAttribute = nullptr; + + const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes(); + for (Qt3DCore::QNodeId attributeId : attributeIds) { + Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId); + if (attribute->attributeType() == QAttribute::IndexAttribute) + indexAttribute = attribute; + else if (command->m_attributes.contains(attribute->nameId())) + estimatedCount = qMax(attribute->count(), estimatedCount); + } + + // Update the draw command with all the information required for the drawing + command->m_drawIndexed = (indexAttribute != nullptr); + if (command->m_drawIndexed) { + command->m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); + command->m_indexAttributeByteOffset = indexAttribute->byteOffset(); + } + + // Use the count specified by the GeometryRender + // If not specified use the indexAttribute count if present + // Otherwise tries to use the count from the attribute with the highest count + if (primitiveCount == 0) { + if (indexAttribute) + primitiveCount = indexAttribute->count(); + else + primitiveCount = estimatedCount; + } + + command->m_primitiveCount = primitiveCount; + command->m_primitiveType = geometryRenderer->primitiveType(); + command->m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled(); + command->m_restartIndexValue = geometryRenderer->restartIndexValue(); + command->m_firstInstance = geometryRenderer->firstInstance(); + command->m_instanceCount = geometryRenderer->instanceCount(); + command->m_firstVertex = geometryRenderer->firstVertex(); + command->m_indexOffset = geometryRenderer->indexOffset(); + command->m_verticesPerPatch = geometryRenderer->verticesPerPatch(); + } + + commands.append(command); + } + } + } + + // We reset the local data once we are done with it + m_localData.setLocalData(nullptr); + + return commands; +} + +QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities) const +{ + // Note: since many threads can be building render commands + // we need to ensure that the UniformBlockValueBuilder they are using + // is only accessed from the same thread + UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); + builder->shaderDataManager = m_manager->shaderDataManager(); + builder->textureManager = m_manager->textureManager(); + m_localData.setLocalData(builder); + + // If the RenderView contains only a ComputeDispatch then it cares about + // A ComputeDispatch is also implicitely a NoDraw operation + // enabled flag + // layer component + // material/effect/technique/parameters/filters/ + QVector<RenderCommand *> commands; + commands.reserve(entities.size()); + for (Entity *entity : entities) { + ComputeCommand *computeJob = nullptr; + if ((computeJob = entity->renderComponent<ComputeCommand>()) != nullptr + && computeJob->isEnabled()) { + + const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); + const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId); + + // 1 RenderCommand per RenderPass pass on an Entity with a Mesh + for (const RenderPassParameterData &passData : renderPassData) { + // Add the RenderPass Parameters + ParameterInfoList globalParameters = passData.parameterInfo; + RenderPass *pass = passData.pass; + parametersFromParametersProvider(&globalParameters, m_manager->parameterManager(), pass); + + RenderCommand *command = new RenderCommand(); + command->m_type = RenderCommand::Compute; + command->m_workGroups[0] = std::max(m_workGroups[0], computeJob->x()); + command->m_workGroups[1] = std::max(m_workGroups[1], computeJob->y()); + command->m_workGroups[2] = std::max(m_workGroups[2], computeJob->z()); + setShaderAndUniforms(command, + pass, + globalParameters, + entity, + QVector<LightSource>(), + nullptr); + commands.append(command); + } + } + } + + // We reset the local data once we are done with it + m_localData.setLocalData(nullptr); + + return commands; +} + +void RenderView::updateMatrices() +{ + if (m_data.m_renderCameraNode && m_data.m_renderCameraLens && m_data.m_renderCameraLens->isEnabled()) { + const Matrix4x4 cameraWorld = *(m_data.m_renderCameraNode->worldTransform()); + setViewMatrix(m_data.m_renderCameraLens->viewMatrix(cameraWorld)); + + setViewProjectionMatrix(m_data.m_renderCameraLens->projection() * viewMatrix()); + //To get the eyePosition of the camera, we need to use the inverse of the + //camera's worldTransform matrix. + const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); + const Vector3D eyePosition(inverseWorldTransform.column(3)); + setEyePosition(eyePosition); + + // Get the viewing direction of the camera. Use the normal matrix to + // ensure non-uniform scale works too. + const QMatrix3x3 normalMat = convertToQMatrix4x4(m_data.m_viewMatrix).normalMatrix(); + // dir = normalize(QVector3D(0, 0, -1) * normalMat) + setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); + } +} + +void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const +{ + // At this point a uniform value can only be a scalar type + // or a Qt3DCore::QNodeId corresponding to a Texture + // ShaderData/Buffers would be handled as UBO/SSBO and would therefore + // not be in the default uniform block + if (value.valueType() == UniformValue::NodeId) { + const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>(); + + const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); + for (int i = 0; i < uniformArraySize; ++i) { + const Qt3DCore::QNodeId texId = nodeIds[i]; + const Texture *tex = m_manager->textureManager()->lookupResource(texId); + if (tex != nullptr) + uniformPack.setTexture(nameId, i, texId); + } + + UniformValue textureValue(uniformArraySize * sizeof(int), UniformValue::TextureValue); + std::fill(textureValue.data<int>(), textureValue.data<int>() + uniformArraySize, -1); + uniformPack.setUniform(nameId, textureValue); + } else { + uniformPack.setUniform(nameId, value); + } +} + +void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, + int glslNameId, + int nameId, + Entity *entity, + const Matrix4x4 &worldTransform) const +{ + uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform)); +} + +void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, + Shader *shader, + const ShaderUniformBlock &block, + const UniformValue &value) const +{ + Q_UNUSED(shader) + + if (value.valueType() == UniformValue::NodeId) { + + Buffer *buffer = nullptr; + if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) { + BlockToUBO uniformBlockUBO; + uniformBlockUBO.m_blockIndex = block.m_index; + uniformBlockUBO.m_bufferID = buffer->peerId(); + uniformBlockUBO.m_needsUpdate = false; + uniformPack.setUniformBuffer(std::move(uniformBlockUBO)); + // Buffer update to GL buffer will be done at render time + } + + + //ShaderData *shaderData = nullptr; + // if ((shaderData = m_manager->shaderDataManager()->lookupResource(value.value<Qt3DCore::QNodeId>())) != nullptr) { + // UBO are indexed by <ShaderId, ShaderDataId> so that a same QShaderData can be used among different shaders + // while still making sure that if they have a different layout everything will still work + // If two shaders define the same block with the exact same layout, in that case the UBO could be shared + // but how do we know that ? We'll need to compare ShaderUniformBlocks + + // Note: we assume that if a buffer is shared across multiple shaders + // then it implies that they share the same layout + + // Temporarly disabled + + // BufferShaderKey uboKey(shaderData->peerId(), + // shader->peerId()); + + // BlockToUBO uniformBlockUBO; + // uniformBlockUBO.m_blockIndex = block.m_index; + // uniformBlockUBO.m_shaderDataID = shaderData->peerId(); + // bool uboNeedsUpdate = false; + + // // build UBO at uboId if not created before + // if (!m_manager->glBufferManager()->contains(uboKey)) { + // m_manager->glBufferManager()->getOrCreateResource(uboKey); + // uboNeedsUpdate = true; + // } + + // // If shaderData has been updated (property has changed or one of the nested properties has changed) + // // foreach property defined in the QShaderData, we try to fill the value of the corresponding active uniform(s) + // // for all the updated properties (all the properties if the UBO was just created) + // if (shaderData->updateViewTransform(*m_data->m_viewMatrix) || uboNeedsUpdate) { + // // Clear previous values remaining in the hash + // m_data->m_uniformBlockBuilder.activeUniformNamesToValue.clear(); + // // Update only update properties if uboNeedsUpdate is true, otherwise update the whole block + // m_data->m_uniformBlockBuilder.updatedPropertiesOnly = uboNeedsUpdate; + // // Retrieve names and description of each active uniforms in the uniform block + // m_data->m_uniformBlockBuilder.uniforms = shader->activeUniformsForUniformBlock(block.m_index); + // // Builds the name-value map for the block + // m_data->m_uniformBlockBuilder.buildActiveUniformNameValueMapStructHelper(shaderData, block.m_name); + // if (!uboNeedsUpdate) + // shaderData->markDirty(); + // // copy the name-value map into the BlockToUBO + // uniformBlockUBO.m_updatedProperties = m_data->m_uniformBlockBuilder.activeUniformNamesToValue; + // uboNeedsUpdate = true; + // } + + // uniformBlockUBO.m_needsUpdate = uboNeedsUpdate; + // uniformPack.setUniformBuffer(std::move(uniformBlockUBO)); + // } + } +} + +void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, + Shader *shader, + const ShaderStorageBlock &block, + const UniformValue &value) const +{ + Q_UNUSED(shader) + if (value.valueType() == UniformValue::NodeId) { + Buffer *buffer = nullptr; + if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) { + BlockToSSBO shaderStorageBlock; + shaderStorageBlock.m_blockIndex = block.m_index; + shaderStorageBlock.m_bufferID = buffer->peerId(); + uniformPack.setShaderStorageBuffer(shaderStorageBlock); + // Buffer update to GL buffer will be done at render time + } + } +} + +void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, Shader *shader, ShaderData *shaderData, const QString &structName) const +{ + UniformBlockValueBuilder *builder = m_localData.localData(); + builder->activeUniformNamesToValue.clear(); + + // Set the view matrix to be used to transform "Transformed" properties in the ShaderData + builder->viewMatrix = m_data.m_viewMatrix; + // Force to update the whole block + builder->updatedPropertiesOnly = false; + // Retrieve names and description of each active uniforms in the uniform block + builder->uniforms = shader->activeUniformsForUniformBlock(-1); + // Build name-value map for the block + builder->buildActiveUniformNameValueMapStructHelper(shaderData, structName); + // Set uniform values for each entrie of the block name-value map + QHash<int, QVariant>::const_iterator activeValuesIt = builder->activeUniformNamesToValue.constBegin(); + const QHash<int, QVariant>::const_iterator activeValuesEnd = builder->activeUniformNamesToValue.constEnd(); + + // TO DO: Make the ShaderData store UniformValue + while (activeValuesIt != activeValuesEnd) { + setUniformValue(uniformPack, activeValuesIt.key(), UniformValue::fromVariant(activeValuesIt.value())); + ++activeValuesIt; + } +} + +void RenderView::setShaderAndUniforms(RenderCommand *command, + RenderPass *rPass, + ParameterInfoList ¶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 + + if (rPass != nullptr) { + // Index Shader by Shader UUID + command->m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(rPass->shaderProgram()); + Shader *shader = m_manager->data<Shader, ShaderManager>(command->m_shader); + if (shader != nullptr && shader->isLoaded()) { + command->m_shaderDna = shader->dna(); + + // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings + // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name + // equals to the parameter name + const QVector<int> uniformNamesIds = shader->uniformsNamesIds(); + const QVector<int> uniformBlockNamesIds = shader->uniformBlockNamesIds(); + const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds(); + const QVector<int> attributeNamesIds = shader->attributeNamesIds(); + + // Set fragData Name and index + // Later on we might want to relink the shader if attachments have changed + // But for now we set them once and for all + QHash<QString, int> fragOutputs; + if (!m_renderTarget.isNull() && !shader->isLoaded()) { + const auto atts = m_attachmentPack.attachments(); + for (const Attachment &att : atts) { + if (att.m_point <= QRenderTargetOutput::Color15) + fragOutputs.insert(att.m_name, att.m_point); + } + } + + if (!uniformNamesIds.isEmpty() || !attributeNamesIds.isEmpty() || + !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) { + + // Set default standard uniforms without bindings + const Matrix4x4 worldTransform = *(entity->worldTransform()); + for (const int uniformNameId : uniformNamesIds) { + if (ms_standardUniformSetters.contains(uniformNameId)) + setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); + } + + // Set default attributes + for (const int attributeNameId : attributeNamesIds) + command->m_attributes.push_back(attributeNameId); + + // Parameters remaining could be + // -> uniform scalar / vector + // -> uniform struct / arrays + // -> uniform block / array (4.3) + // -> ssbo block / array (4.3) + + ParameterInfoList::const_iterator it = parameters.cbegin(); + const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); + + while (it != parametersEnd) { + Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); + const UniformValue &uniformValue = param->uniformValue(); + if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform + setUniformValue(command->m_parameterPack, it->nameId, uniformValue); + } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block + setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue); + } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO + setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue); + } else { // Parameter is a struct + ShaderData *shaderData = nullptr; + if (uniformValue.valueType() == UniformValue::NodeId && + (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { + // Try to check if we have a struct or array matching a QShaderData parameter + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId)); + } + // Otherwise: param unused by current shader + } + ++it; + } + + // Lights + + int lightIdx = 0; + for (const LightSource &lightSource : activeLightSources) { + if (lightIdx == MAX_LIGHTS) + break; + Entity *lightEntity = lightSource.entity; + const Vector3D worldPos = lightEntity->worldBoundingVolume()->center(); + for (Light *light : lightSource.lights) { + if (!light->isEnabled()) + continue; + + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); + if (!shaderData) + continue; + + if (lightIdx == MAX_LIGHTS) + break; + + // Note: implicit conversion of values to UniformValue + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); + + // There is no risk in doing that even if multithreaded + // since we are sure that a shaderData is unique for a given light + // and won't ever be referenced as a Component either + Matrix4x4 *worldTransform = lightEntity->worldTransform(); + if (worldTransform) + shaderData->updateWorldTransform(*worldTransform); + + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]); + ++lightIdx; + } + } + + if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID)) + setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax(1, lightIdx))); + + // If no active light sources and no environment light, add a default light + if (activeLightSources.isEmpty() && !environmentLight) { + // Note: implicit conversion of values to UniformValue + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); + } + + // Environment Light + int envLightCount = 0; + if (environmentLight && environmentLight->isEnabled()) { + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); + if (shaderData) { + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); + envLightCount = 1; + } + } + setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); + } + // Set frag outputs in the shaders if hash not empty + if (!fragOutputs.isEmpty()) + shader->setFragOutputs(fragOutputs); + } + } + else { + qCWarning(Render::Backend) << Q_FUNC_INFO << "Using default effect as none was provided"; + } +} + +bool RenderView::hasBlitFramebufferInfo() const +{ + return m_hasBlitFramebufferInfo; +} + +void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) +{ + m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; +} + +BlitFramebufferInfo RenderView::blitFrameBufferInfo() const +{ + return m_blitFrameBufferInfo; +} + +void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo) +{ + m_blitFrameBufferInfo = blitFrameBufferInfo; +} + +bool RenderView::isDownloadBuffersEnable() const +{ + return m_isDownloadBuffersEnable; +} + +void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) +{ + m_isDownloadBuffersEnable = isDownloadBuffersEnable; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/renderview_p.h b/src/render/renderers/opengl/renderer/renderview_p.h new file mode 100644 index 000000000..cb3c74917 --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderview_p.h @@ -0,0 +1,392 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERVIEW_H +#define QT3DRENDER_RENDER_RENDERVIEW_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qparameter.h> +#include <Qt3DRender/qclearbuffers.h> +#include <Qt3DRender/qlayerfilter.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/clearbuffers_p.h> +#include <Qt3DRender/private/cameralens_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/qsortpolicy_p.h> +#include <Qt3DRender/private/lightsource_p.h> +#include <Qt3DRender/private/qmemorybarrier_p.h> +#include <Qt3DRender/private/qrendercapture_p.h> +#include <Qt3DRender/private/qblitframebuffer_p.h> + +#include <Qt3DCore/private/qframeallocator_p.h> +#include <Qt3DRender/private/aligned_malloc_p.h> + +// TODO: Move out once this is all refactored +#include <Qt3DRender/private/renderviewjobutils_p.h> + +#include <QVector> +#include <QSurface> +#include <QMutex> +#include <QColor> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +class QRenderPass; + +namespace Render { + +class Renderer; +class NodeManagers; +class RenderCommand; +class RenderPassFilter; +class TechniqueFilter; +class ViewportNode; +class Effect; +class RenderPass; + +typedef QPair<ShaderUniform, QVariant> ActivePropertyContent; +typedef QPair<QString, ActivePropertyContent > ActiveProperty; + +struct Q_AUTOTEST_EXPORT Plane +{ + explicit Plane(const Vector4D &planeEquation) + : planeEquation(planeEquation) + , normal(Vector3D(planeEquation).normalized()) + , d(planeEquation.w() / Vector3D(planeEquation).length()) + {} + + const Vector4D planeEquation; + const Vector3D normal; + const float d; +}; + +struct Q_AUTOTEST_EXPORT ClearBufferInfo +{ + int drawBufferIndex = 0; + QRenderTargetOutput::AttachmentPoint attchmentPoint = QRenderTargetOutput::Color0; + QVector4D clearColor; +}; + +struct Q_AUTOTEST_EXPORT BlitFramebufferInfo +{ + Qt3DCore::QNodeId sourceRenderTargetId; + Qt3DCore::QNodeId destinationRenderTargetId; + QRect sourceRect; + QRect destinationRect; + Qt3DRender::QRenderTargetOutput::AttachmentPoint sourceAttachmentPoint; + Qt3DRender::QRenderTargetOutput::AttachmentPoint destinationAttachmentPoint; + QBlitFramebuffer::InterpolationMethod interpolationMethod; +}; + +// This class is kind of analogous to RenderBin but I want to avoid trampling +// on that until we get this working + +class Q_AUTOTEST_EXPORT RenderView +{ +public: + RenderView(); + ~RenderView(); + + QT3D_ALIGNED_MALLOC_AND_FREE() + + // TODO: Add a way to specify a sort predicate for the RenderCommands + void sort(); + + void setRenderer(Renderer *renderer); + inline void setSurfaceSize(const QSize &size) Q_DECL_NOTHROW { m_surfaceSize = size; } + inline Renderer *renderer() const Q_DECL_NOTHROW { return m_renderer; } + inline NodeManagers *nodeManagers() const Q_DECL_NOTHROW { return m_manager; } + inline const QSize &surfaceSize() const Q_DECL_NOTHROW { return m_surfaceSize; } + inline void setDevicePixelRatio(qreal r) Q_DECL_NOTHROW { m_devicePixelRatio = r; } + inline qreal devicePixelRatio() const Q_DECL_NOTHROW { return m_devicePixelRatio; } + + inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_data.m_renderCameraLens = renderCameraLens; } + inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_data.m_renderCameraLens; } + + inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_data.m_renderCameraNode = renderCameraNode; } + inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_data.m_renderCameraNode; } + + inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_data.m_viewMatrix = viewMatrix; } + inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; } + + inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_data.m_viewProjectionMatrix = viewProjectionMatrix; } + inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_data.m_viewProjectionMatrix; } + + inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; } + inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; } + + inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_data.m_eyeViewDir = dir; } + inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; } + + inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_data.m_layerFilterIds.push_back(layerFilterId); } + inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_data.m_layerFilterIds; } + + inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_data.m_proximityFilterIds.push_back(proximityFilterId); } + inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_data.m_proximityFilterIds; } + + inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_data.m_passFilter = rpFilter; } + inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_data.m_passFilter; } + + inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_data.m_techniqueFilter = filter; } + inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_data.m_techniqueFilter; } + + inline RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet; } + void setStateSet(RenderStateSet *stateSet) Q_DECL_NOTHROW { m_stateSet = stateSet; } + + inline bool noDraw() const Q_DECL_NOTHROW { return m_noDraw; } + void setNoDraw(bool noDraw) Q_DECL_NOTHROW { m_noDraw = noDraw; } + + inline bool isCompute() const Q_DECL_NOTHROW { return m_compute; } + void setCompute(bool compute) Q_DECL_NOTHROW { m_compute = compute; } + + void setComputeWorkgroups(int x, int y, int z) Q_DECL_NOTHROW { m_workGroups[0] = x; m_workGroups[1] = y; m_workGroups[2] = z; } + const int *computeWorkGroups() const Q_DECL_NOTHROW { return m_workGroups; } + inline bool frustumCulling() const Q_DECL_NOTHROW { return m_frustumCulling; } + void setFrustumCulling(bool frustumCulling) Q_DECL_NOTHROW { m_frustumCulling = frustumCulling; } + + inline void setMaterialParameterTable(const MaterialParameterGathererData ¶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); + + QVector<RenderCommand *> buildDrawRenderCommands(const QVector<Entity *> &entities) const; + QVector<RenderCommand *> buildComputeRenderCommands(const QVector<Entity *> &entities) const; + void setCommands(QVector<RenderCommand *> &commands) Q_DECL_NOTHROW { m_commands = commands; } + QVector<RenderCommand *> commands() const Q_DECL_NOTHROW { return m_commands; } + + void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; } + const AttachmentPack &attachmentPack() const { return m_attachmentPack; } + + void setRenderTargetId(Qt3DCore::QNodeId renderTargetId) Q_DECL_NOTHROW { m_renderTarget = renderTargetId; } + Qt3DCore::QNodeId renderTargetId() const Q_DECL_NOTHROW { return m_renderTarget; } + + void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_data.m_sortingTypes.append(sortTypes); } + + void setSurface(QSurface *surface) { m_surface = surface; } + QSurface *surface() const { return m_surface; } + + void setLightSources(const QVector<LightSource> &lightSources) Q_DECL_NOTHROW { m_lightSources = lightSources; } + void setEnvironmentLight(EnvironmentLight *environmentLight) Q_DECL_NOTHROW { m_environmentLight = environmentLight; } + + void updateMatrices(); + + inline void setRenderCaptureNodeId(const Qt3DCore::QNodeId nodeId) Q_DECL_NOTHROW { m_renderCaptureNodeId = nodeId; } + inline const Qt3DCore::QNodeId renderCaptureNodeId() const Q_DECL_NOTHROW { return m_renderCaptureNodeId; } + inline void setRenderCaptureRequest(const QRenderCaptureRequest& request) Q_DECL_NOTHROW { m_renderCaptureRequest = request; } + inline const QRenderCaptureRequest renderCaptureRequest() const Q_DECL_NOTHROW { return m_renderCaptureRequest; } + + void setMemoryBarrier(QMemoryBarrier::Operations barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; } + QMemoryBarrier::Operations memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; } + + // Helps making the size of RenderView smaller + // Contains all the data needed for the actual building of the RenderView + // But that aren't used later by the Renderer + struct InnerData { + InnerData() + : m_renderCameraLens(nullptr) + , m_renderCameraNode(nullptr) + , m_techniqueFilter(nullptr) + , m_passFilter(nullptr) + { + } + CameraLens *m_renderCameraLens; + Entity *m_renderCameraNode; + const TechniqueFilter *m_techniqueFilter; + const RenderPassFilter *m_passFilter; + Matrix4x4 m_viewMatrix; + Matrix4x4 m_viewProjectionMatrix; + Qt3DCore::QNodeIdVector m_layerFilterIds; + QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes; + Vector3D m_eyePos; + Vector3D m_eyeViewDir; + Qt3DCore::QNodeIdVector m_proximityFilterIds; + }; + + bool isDownloadBuffersEnable() const; + void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable); + + BlitFramebufferInfo blitFrameBufferInfo() const; + void setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo); + + bool hasBlitFramebufferInfo() const; + void setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo); + +private: + void setShaderAndUniforms(RenderCommand *command, + RenderPass *pass, + ParameterInfoList ¶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; + + // We do not use pointers to RenderNodes or Drawable's here so that the + // render aspect is free to change the drawables on the next frame whilst + // the render thread is submitting these commands. + QVector<RenderCommand *> m_commands; + mutable QVector<LightSource> m_lightSources; + EnvironmentLight *m_environmentLight; + + MaterialParameterGathererData m_parameters; + + enum StandardUniform + { + ModelMatrix, + ViewMatrix, + ProjectionMatrix, + ModelViewMatrix, + ViewProjectionMatrix, + ModelViewProjectionMatrix, + InverseModelMatrix, + InverseViewMatrix, + InverseProjectionMatrix, + InverseModelViewMatrix, + InverseViewProjectionMatrix, + InverseModelViewProjectionMatrix, + ModelNormalMatrix, + ModelViewNormalMatrix, + ViewportMatrix, + InverseViewportMatrix, + AspectRatio, + Time, + Exposure, + Gamma, + EyePosition, + SkinningPalette + }; + + typedef QHash<int, StandardUniform> StandardUniformsNameToTypeHash; + static StandardUniformsNameToTypeHash ms_standardUniformSetters; + static StandardUniformsNameToTypeHash initializeStandardUniformSetters(); + + UniformValue standardUniformValue(StandardUniform standardUniformType, + Entity *entity, + const Matrix4x4 &model) const; + + void setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const; + void setStandardUniformValue(ShaderParameterPack &uniformPack, + int glslNameId, + int nameId, + Entity *entity, + const Matrix4x4 &worldTransform) const; + void setUniformBlockValue(ShaderParameterPack &uniformPack, + Shader *shader, + const ShaderUniformBlock &block, + const UniformValue &value) const; + void setShaderStorageValue(ShaderParameterPack &uniformPack, + Shader *shader, + const ShaderStorageBlock &block, + const UniformValue &value) const; + void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, + Shader *shader, + ShaderData *shaderData, + const QString &structName) const; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEW_H diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp new file mode 100644 index 000000000..d08bd6dd4 --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp @@ -0,0 +1,672 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderviewbuilder_p.h" + +#include <QThread> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +const int RenderViewBuilder::m_optimalParallelJobCount = std::max(QThread::idealThreadCount(), 2); + +namespace { + +class SyncRenderViewCommandBuilders +{ +public: + explicit SyncRenderViewCommandBuilders(const RenderViewInitializerJobPtr &renderViewJob, + const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs, + Renderer *renderer) + : m_renderViewJob(renderViewJob) + , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderer(renderer) + {} + + void operator()() + { + // Append all the commands and sort them + RenderView *rv = m_renderViewJob->renderView(); + + int totalCommandCount = 0; + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + totalCommandCount += renderViewCommandBuilder->commands().size(); + + QVector<RenderCommand *> commands; + commands.reserve(totalCommandCount); + + // Reduction + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + commands += std::move(renderViewCommandBuilder->commands()); + rv->setCommands(commands); + + // Sort the commands + rv->sort(); + + // Enqueue our fully populated RenderView with the RenderThread + m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex()); + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + Renderer *m_renderer; +}; + +class SyncFrustumCulling +{ +public: + explicit SyncFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCulling) + : m_renderViewJob(renderViewJob) + , m_frustumCullingJob(frustumCulling) + {} + + void operator()() + { + RenderView *rv = m_renderViewJob->renderView(); + + // Update matrices now that all transforms have been updated + rv->updateMatrices(); + + // Frustum culling + m_frustumCullingJob->setViewProjection(rv->viewProjectionMatrix()); + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + FrustumCullingJobPtr m_frustumCullingJob; +}; + +class SyncRenderViewInitialization +{ +public: + explicit SyncRenderViewInitialization(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterLayerEntityJobPtr &filterEntityByLayerJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs) + : m_renderViewJob(renderViewJob) + , m_frustumCullingJob(frustumCullingJob) + , m_filterEntityByLayerJob(filterEntityByLayerJob) + , m_filterProximityJob(filterProximityJob) + , m_materialGathererJobs(materialGathererJobs) + , m_renderViewBuilderJobs(renderViewBuilderJobs) + {} + + void operator()() + { + RenderView *rv = m_renderViewJob->renderView(); + + // Layer filtering + if (!m_filterEntityByLayerJob.isNull()) + m_filterEntityByLayerJob->setLayerFilters(rv->layerFilters()); + + // Proximity filtering + m_filterProximityJob->setProximityFilterIds(rv->proximityFilterIds()); + + // Material Parameter building + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { + materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter())); + materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter())); + } + + // Command builders + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + renderViewCommandBuilder->setRenderView(rv); + + // Set whether frustum culling is enabled or not + m_frustumCullingJob->setActive(rv->frustumCulling()); + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + FrustumCullingJobPtr m_frustumCullingJob; + FilterLayerEntityJobPtr m_filterEntityByLayerJob; + FilterProximityDistanceJobPtr m_filterProximityJob; + QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; + QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; +}; + +class SyncRenderCommandBuilding +{ +public: + explicit SyncRenderCommandBuilding(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const LightGathererPtr &lightGathererJob, + const RenderableEntityFilterPtr &renderableEntityFilterJob, + const ComputableEntityFilterPtr &computableEntityFilterJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_renderViewJob(renderViewJob) + , m_frustumCullingJob(frustumCullingJob) + , m_filterProximityJob(filterProximityJob) + , m_lightGathererJob(lightGathererJob) + , m_renderableEntityFilterJob(renderableEntityFilterJob) + , m_computableEntityFilterJob(computableEntityFilterJob) + , m_materialGathererJobs(materialGathererJobs) + , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderer(renderer) + , m_leafNode(leafNode) + {} + + void operator()() + { + // Set the result of previous job computations + // for final RenderCommand building + RenderView *rv = m_renderViewJob->renderView(); + + if (!rv->noDraw()) { + rv->setEnvironmentLight(m_lightGathererJob->takeEnvironmentLight()); + + // We sort the vector so that the removal can then be performed linearly + + QVector<Entity *> renderableEntities; + const bool isDraw = !rv->isCompute(); + if (isDraw) + renderableEntities = std::move(m_renderableEntityFilterJob->filteredEntities()); + else + renderableEntities = std::move(m_computableEntityFilterJob->filteredEntities()); + + // Filter out entities that weren't selected by the layer filters + std::sort(renderableEntities.begin(), renderableEntities.end()); + + QMutexLocker lock(m_renderer->cache()->mutex()); + const QVector<Entity *> filteredEntities = m_renderer->cache()->leafNodeCache.value(m_leafNode).filterEntitiesByLayer; + lock.unlock(); + // Remove all entities from the compute and renderable vectors that aren't in the filtered layer vector + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, filteredEntities); + + // Set the light sources, with layer filters applied. + QVector<LightSource> lightSources = m_lightGathererJob->lights(); + for (int i = 0; i < lightSources.count(); ++i) { + if (!filteredEntities.contains(lightSources[i].entity)) + lightSources.removeAt(i--); + } + rv->setLightSources(lightSources); + + if (isDraw) { + // Filter out frustum culled entity for drawable entities + if (rv->frustumCulling()) + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_frustumCullingJob->visibleEntities()); + // Filter out entities which didn't satisfy proximity filtering + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities()); + } + + // Split among the number of command builders + int i = 0; + const int m = RenderViewBuilder::optimalJobCount() - 1; + const int packetSize = renderableEntities.size() / RenderViewBuilder::optimalJobCount(); + while (i < m) { + const RenderViewBuilderJobPtr renderViewCommandBuilder = m_renderViewBuilderJobs.at(i); + renderViewCommandBuilder->setRenderables(renderableEntities.mid(i * packetSize, packetSize)); + ++i; + } + m_renderViewBuilderJobs.at(i)->setRenderables(renderableEntities.mid(i * packetSize, packetSize + renderableEntities.size() % (m + 1))); + { + QMutexLocker rendererCacheLock(m_renderer->cache()->mutex()); + rv->setMaterialParameterTable(m_renderer->cache()->leafNodeCache.value(m_leafNode).materialParameterGatherer); + } + } + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; + FrustumCullingJobPtr m_frustumCullingJob; + FilterProximityDistanceJobPtr m_filterProximityJob; + LightGathererPtr m_lightGathererJob; + RenderableEntityFilterPtr m_renderableEntityFilterJob; + ComputableEntityFilterPtr m_computableEntityFilterJob; + QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; + QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +class SetClearDrawBufferIndex +{ +public: + explicit SetClearDrawBufferIndex(const RenderViewInitializerJobPtr &renderViewJob) + : m_renderViewJob(renderViewJob) + {} + + void operator()() + { + RenderView *rv = m_renderViewJob->renderView(); + QVector<ClearBufferInfo> &clearBuffersInfo = rv->specificClearColorBufferInfo(); + const AttachmentPack &attachmentPack = rv->attachmentPack(); + for (ClearBufferInfo &clearBufferInfo : clearBuffersInfo) + clearBufferInfo.drawBufferIndex = attachmentPack.getDrawBufferIndex(clearBufferInfo.attchmentPoint); + + } + +private: + RenderViewInitializerJobPtr m_renderViewJob; +}; + +class SyncFilterEntityByLayer +{ +public: + explicit SyncFilterEntityByLayer(const FilterLayerEntityJobPtr &filterEntityByLayerJob, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_filterEntityByLayerJob(filterEntityByLayerJob) + , m_renderer(renderer) + , m_leafNode(leafNode) + { + } + + void operator()() + { + QMutexLocker lock(m_renderer->cache()->mutex()); + // Save the filtered by layer subset into the cache + const QVector<Entity *> filteredEntities = m_filterEntityByLayerJob->filteredEntities(); + RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; + dataCacheForLeaf.filterEntitiesByLayer = filteredEntities; + } + +private: + FilterLayerEntityJobPtr m_filterEntityByLayerJob; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +class SyncMaterialParameterGatherer +{ +public: + explicit SyncMaterialParameterGatherer(const QVector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_materialParameterGathererJobs(materialParameterGathererJobs) + , m_renderer(renderer) + , m_leafNode(leafNode) + { + } + + void operator()() + { + QMutexLocker lock(m_renderer->cache()->mutex()); + RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; + dataCacheForLeaf.materialParameterGatherer.clear(); + + for (const auto &materialGatherer : qAsConst(m_materialParameterGathererJobs)) + dataCacheForLeaf.materialParameterGatherer.unite(materialGatherer->materialToPassAndParameter()); + } + +private: + QVector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +} // anonymous + +RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer) + : m_leafNode(leafNode) + , m_renderViewIndex(renderViewIndex) + , m_renderer(renderer) + , m_layerCacheNeedsToBeRebuilt(false) + , m_materialGathererCacheNeedsToBeRebuilt(false) + , m_renderViewJob(RenderViewInitializerJobPtr::create()) + , m_filterEntityByLayerJob() + , m_lightGathererJob(Render::LightGathererPtr::create()) + , m_renderableEntityFilterJob(RenderableEntityFilterPtr::create()) + , m_computableEntityFilterJob(ComputableEntityFilterPtr::create()) + , m_frustumCullingJob(new Render::FrustumCullingJob()) + , m_syncFrustumCullingJob(SynchronizerJobPtr::create(SyncFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) + , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) + , m_syncFilterEntityByLayerJob() + , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create()) +{ +} + +RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const +{ + return m_renderViewJob; +} + +FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const +{ + return m_filterEntityByLayerJob; +} + +LightGathererPtr RenderViewBuilder::lightGathererJob() const +{ + return m_lightGathererJob; +} + +RenderableEntityFilterPtr RenderViewBuilder::renderableEntityFilterJob() const +{ + return m_renderableEntityFilterJob; +} + +ComputableEntityFilterPtr RenderViewBuilder::computableEntityFilterJob() const +{ + return m_computableEntityFilterJob; +} + +FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const +{ + return m_frustumCullingJob; +} + +QVector<RenderViewBuilderJobPtr> RenderViewBuilder::renderViewBuilderJobs() const +{ + return m_renderViewBuilderJobs; +} + +QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const +{ + return m_materialGathererJobs; +} + +SynchronizerJobPtr RenderViewBuilder::syncRenderViewInitializationJob() const +{ + return m_syncRenderViewInitializationJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncFrustumCullingJob() const +{ + return m_syncFrustumCullingJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncRenderCommandBuildingJob() const +{ + return m_syncRenderCommandBuildingJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncRenderViewCommandBuildersJob() const +{ + return m_syncRenderViewCommandBuildersJob; +} + +SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const +{ + return m_setClearDrawBufferIndexJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const +{ + return m_syncFilterEntityByLayerJob; +} + +SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const +{ + return m_syncMaterialGathererJob; +} + +FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const +{ + return m_filterProximityJob; +} + +void RenderViewBuilder::prepareJobs() +{ + // Init what we can here + EntityManager *entityManager = m_renderer->nodeManagers()->renderNodesManager(); + m_filterProximityJob->setManager(m_renderer->nodeManagers()); + m_renderableEntityFilterJob->setManager(entityManager); + m_computableEntityFilterJob->setManager(entityManager); + m_frustumCullingJob->setRoot(m_renderer->sceneRoot()); + m_lightGathererJob->setManager(entityManager); + m_renderViewJob->setRenderer(m_renderer); + m_renderViewJob->setFrameGraphLeafNode(m_leafNode); + m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex); + + // RenderCommand building is the most consuming task -> split it + // Estimate the number of jobs to create based on the number of entities + m_renderViewBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { + auto renderViewCommandBuilder = Render::RenderViewBuilderJobPtr::create(); + renderViewCommandBuilder->setIndex(m_renderViewIndex); + renderViewCommandBuilder->setRenderer(m_renderer); + m_renderViewBuilderJobs.push_back(renderViewCommandBuilder); + } + + if (m_materialGathererCacheNeedsToBeRebuilt) { + // Since Material gathering is an heavy task, we split it + const QVector<HMaterial> materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles(); + const int elementsPerJob = materialHandles.size() / RenderViewBuilder::m_optimalParallelJobCount; + const int lastRemaingElements = materialHandles.size() % RenderViewBuilder::m_optimalParallelJobCount; + m_materialGathererJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { + auto materialGatherer = Render::MaterialParameterGathererJobPtr::create(); + materialGatherer->setNodeManagers(m_renderer->nodeManagers()); + materialGatherer->setRenderer(m_renderer); + if (i == RenderViewBuilder::m_optimalParallelJobCount - 1) + materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob + lastRemaingElements)); + else + materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob)); + m_materialGathererJobs.push_back(materialGatherer); + } + m_syncMaterialGathererJob = SynchronizerJobPtr::create(SyncMaterialParameterGatherer(m_materialGathererJobs, + m_renderer, + m_leafNode), + JobTypes::SyncMaterialGatherer); + } + + if (m_layerCacheNeedsToBeRebuilt) { + m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create(); + m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers()); + m_syncFilterEntityByLayerJob = SynchronizerJobPtr::create(SyncFilterEntityByLayer(m_filterEntityByLayerJob, + m_renderer, + m_leafNode), + JobTypes::SyncFilterEntityByLayer); + } + + m_syncRenderCommandBuildingJob = SynchronizerJobPtr::create(SyncRenderCommandBuilding(m_renderViewJob, + m_frustumCullingJob, + m_filterProximityJob, + m_lightGathererJob, + m_renderableEntityFilterJob, + m_computableEntityFilterJob, + m_materialGathererJobs, + m_renderViewBuilderJobs, + m_renderer, + m_leafNode), + JobTypes::SyncRenderViewCommandBuilding); + + m_syncRenderViewCommandBuildersJob = SynchronizerJobPtr::create(SyncRenderViewCommandBuilders(m_renderViewJob, + m_renderViewBuilderJobs, + m_renderer), + JobTypes::SyncRenderViewCommandBuilder); + + m_syncRenderViewInitializationJob = SynchronizerJobPtr::create(SyncRenderViewInitialization(m_renderViewJob, + m_frustumCullingJob, + m_filterEntityByLayerJob, + m_filterProximityJob, + m_materialGathererJobs, + m_renderViewBuilderJobs), + JobTypes::SyncRenderViewInitialization); + +} + +QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const +{ + QVector<Qt3DCore::QAspectJobPtr> jobs; + + jobs.reserve(m_materialGathererJobs.size() + m_renderViewBuilderJobs.size() + 11); + + // Set dependencies + + // Finish the skinning palette job before processing renderviews + // TODO: Maybe only update skinning palettes for non-culled entities + m_renderViewJob->addDependency(m_renderer->updateSkinningPaletteJob()); + + m_syncFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob()); + m_syncFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob()); + m_syncFrustumCullingJob->addDependency(m_syncRenderViewInitializationJob); + + m_frustumCullingJob->addDependency(m_renderer->expandBoundingVolumeJob()); + m_frustumCullingJob->addDependency(m_syncFrustumCullingJob); + + m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewInitializationJob); + + m_syncRenderViewInitializationJob->addDependency(m_renderViewJob); + + m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob()); + m_filterProximityJob->addDependency(m_syncRenderViewInitializationJob); + + m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob); + m_syncRenderCommandBuildingJob->addDependency(m_renderableEntityFilterJob); + m_syncRenderCommandBuildingJob->addDependency(m_computableEntityFilterJob); + m_syncRenderCommandBuildingJob->addDependency(m_filterProximityJob); + m_syncRenderCommandBuildingJob->addDependency(m_lightGathererJob); + m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob); + + // Ensure the RenderThread won't be able to process dirtyResources + // before they have been completely gathered + m_syncRenderCommandBuildingJob->addDependency(m_renderer->introspectShadersJob()); + m_syncRenderCommandBuildingJob->addDependency(m_renderer->bufferGathererJob()); + m_syncRenderCommandBuildingJob->addDependency(m_renderer->textureGathererJob()); + + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { + renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob); + m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder); + } + + m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewCommandBuildersJob); + m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob); + + // Add jobs + jobs.push_back(m_renderViewJob); // Step 1 + jobs.push_back(m_renderableEntityFilterJob); // Step 1 + jobs.push_back(m_lightGathererJob); // Step 1 + + // Note: do it only if OpenGL 4.3+ available + jobs.push_back(m_computableEntityFilterJob); // Step 1 + + jobs.push_back(m_syncRenderViewInitializationJob); // Step 2 + + if (m_layerCacheNeedsToBeRebuilt) { + m_filterEntityByLayerJob->addDependency(m_syncRenderViewInitializationJob); + m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob()); + + m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob); + m_syncRenderCommandBuildingJob->addDependency(m_syncFilterEntityByLayerJob); + + jobs.push_back(m_filterEntityByLayerJob); // Step 3 + jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4 + } + jobs.push_back(m_syncFrustumCullingJob); // Step 3 + jobs.push_back(m_filterProximityJob); // Step 3 + jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3 + + if (m_materialGathererCacheNeedsToBeRebuilt) { + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { + materialGatherer->addDependency(m_syncRenderViewInitializationJob); + materialGatherer->addDependency(m_renderer->introspectShadersJob()); + materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob()); + jobs.push_back(materialGatherer); // Step3 + m_syncMaterialGathererJob->addDependency(materialGatherer); + } + m_syncRenderCommandBuildingJob->addDependency(m_syncMaterialGathererJob); + + jobs.push_back(m_syncMaterialGathererJob); // Step 3 + } + + jobs.push_back(m_frustumCullingJob); // Step 4 + jobs.push_back(m_syncRenderCommandBuildingJob); // Step 5 + + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 6 + jobs.push_back(renderViewCommandBuilder); + + jobs.push_back(m_syncRenderViewCommandBuildersJob); // Step 7 + + return jobs; +} + +Renderer *RenderViewBuilder::renderer() const +{ + return m_renderer; +} + +int RenderViewBuilder::renderViewIndex() const +{ + return m_renderViewIndex; +} + +void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt) +{ + m_layerCacheNeedsToBeRebuilt = needsToBeRebuilt; +} + +bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const +{ + return m_layerCacheNeedsToBeRebuilt; +} + +void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt) +{ + m_materialGathererCacheNeedsToBeRebuilt = needsToBeRebuilt; +} + +bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const +{ + return m_materialGathererCacheNeedsToBeRebuilt; +} + +int RenderViewBuilder::optimalJobCount() +{ + return RenderViewBuilder::m_optimalParallelJobCount; +} + +QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset) +{ + QVector<Entity *> intersection; + intersection.reserve(qMin(entities.size(), subset.size())); + std::set_intersection(entities.begin(), entities.end(), + subset.begin(), subset.end(), + std::back_inserter(intersection)); + + return intersection; +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder_p.h b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h new file mode 100644 index 000000000..818313500 --- /dev/null +++ b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERVIEWBUILDER_H +#define QT3DRENDER_RENDER_RENDERVIEWBUILDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <functional> +#include <Qt3DCore/qaspectjob.h> +#include <Qt3DRender/private/filterentitybycomponentjob_p.h> +#include <Qt3DRender/private/filterlayerentityjob_p.h> +#include <Qt3DRender/private/genericlambdajob_p.h> +#include <Qt3DRender/private/materialparametergathererjob_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/renderviewbuilderjob_p.h> +#include <Qt3DRender/private/renderview_p.h> +#include <Qt3DRender/private/frustumcullingjob_p.h> +#include <Qt3DRender/private/lightgatherer_p.h> +#include <Qt3DRender/private/filterproximitydistancejob_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +class Renderer; + +using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; +using ComputableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::ComputeCommand, Render::Material>; +using RenderableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::GeometryRenderer, Render::Material>; + +class Q_AUTOTEST_EXPORT RenderViewBuilder +{ +public: + explicit RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer); + + RenderViewInitializerJobPtr renderViewJob() const; + FilterLayerEntityJobPtr filterEntityByLayerJob() const; + LightGathererPtr lightGathererJob() const; + RenderableEntityFilterPtr renderableEntityFilterJob() const; + ComputableEntityFilterPtr computableEntityFilterJob() const; + FrustumCullingJobPtr frustumCullingJob() const; + QVector<RenderViewBuilderJobPtr> renderViewBuilderJobs() const; + QVector<MaterialParameterGathererJobPtr> materialGathererJobs() const; + SynchronizerJobPtr syncRenderViewInitializationJob() const; + SynchronizerJobPtr syncFrustumCullingJob() const; + SynchronizerJobPtr syncRenderCommandBuildingJob() const; + SynchronizerJobPtr syncRenderViewCommandBuildersJob() const; + SynchronizerJobPtr setClearDrawBufferIndexJob() const; + SynchronizerJobPtr syncFilterEntityByLayerJob() const; + FilterProximityDistanceJobPtr filterProximityJob() const; + SynchronizerJobPtr syncMaterialGathererJob() const; + + void prepareJobs(); + QVector<Qt3DCore::QAspectJobPtr> buildJobHierachy() const; + + Renderer *renderer() const; + int renderViewIndex() const; + + void setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt); + bool layerCacheNeedsToBeRebuilt() const; + void setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt); + bool materialGathererCacheNeedsToBeRebuilt() const; + + static int optimalJobCount(); + static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset); + +private: + Render::FrameGraphNode *m_leafNode; + const int m_renderViewIndex; + Renderer *m_renderer; + bool m_layerCacheNeedsToBeRebuilt; + bool m_materialGathererCacheNeedsToBeRebuilt; + + RenderViewInitializerJobPtr m_renderViewJob; + FilterLayerEntityJobPtr m_filterEntityByLayerJob; + LightGathererPtr m_lightGathererJob; + RenderableEntityFilterPtr m_renderableEntityFilterJob; + ComputableEntityFilterPtr m_computableEntityFilterJob; + FrustumCullingJobPtr m_frustumCullingJob; + QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; + + SynchronizerJobPtr m_syncRenderViewInitializationJob; + SynchronizerJobPtr m_syncFrustumCullingJob; + SynchronizerJobPtr m_syncRenderCommandBuildingJob; + SynchronizerJobPtr m_syncRenderViewCommandBuildersJob; + SynchronizerJobPtr m_setClearDrawBufferIndexJob; + SynchronizerJobPtr m_syncFilterEntityByLayerJob; + SynchronizerJobPtr m_syncMaterialGathererJob; + FilterProximityDistanceJobPtr m_filterProximityJob; + + static const int m_optimalParallelJobCount; +}; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERVIEWBUILDER_H diff --git a/src/render/renderers/opengl/renderer/shaderparameterpack.cpp b/src/render/renderers/opengl/renderer/shaderparameterpack.cpp new file mode 100644 index 000000000..f78e45a5e --- /dev/null +++ b/src/render/renderers/opengl/renderer/shaderparameterpack.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shaderparameterpack_p.h" + +#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/texture_p.h> + +#include <Qt3DCore/private/qframeallocator_p.h> + +#include <QOpenGLShaderProgram> +#include <QDebug> +#include <QColor> +#include <QQuaternion> +#include <Qt3DRender/private/renderlogging_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +ShaderParameterPack::~ShaderParameterPack() +{ + m_uniforms.clear(); +} + +void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val) +{ + m_uniforms.insert(glslNameId, val); +} + +void ShaderParameterPack::setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId texId) +{ + for (int t=0; t<m_textures.size(); ++t) { + if (m_textures[t].glslNameId != glslNameId || m_textures[t].uniformArrayIndex != uniformArrayIndex) + continue; + + m_textures[t].texId = texId; + return; + } + + m_textures.append(NamedTexture(glslNameId, texId, uniformArrayIndex)); +} + +// Contains Uniform Block Index and QNodeId of the ShaderData (UBO) +void ShaderParameterPack::setUniformBuffer(BlockToUBO blockToUBO) +{ + m_uniformBuffers.append(std::move(blockToUBO)); +} + +void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO) +{ + m_shaderStorageBuffers.push_back(std::move(blockToSSBO)); +} + +void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform) +{ + m_submissionUniforms.push_back(uniform); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderer/shaderparameterpack_p.h b/src/render/renderers/opengl/renderer/shaderparameterpack_p.h new file mode 100644 index 000000000..5703bb17b --- /dev/null +++ b/src/render/renderers/opengl/renderer/shaderparameterpack_p.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H +#define QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QVariant> +#include <QByteArray> +#include <QVector> +#include <QOpenGLShaderProgram> +#include <Qt3DCore/qnodeid.h> +#include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/shadervariables_p.h> +#include <Qt3DRender/private/uniform_p.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; + +namespace Qt3DCore { +class QFrameAllocator; +} + +namespace Qt3DRender { +namespace Render { + +class GraphicsContext; + +struct BlockToUBO { + int m_blockIndex; + Qt3DCore::QNodeId m_bufferID; + bool m_needsUpdate; + QHash<QString, QVariant> m_updatedProperties; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToUBO, Q_MOVABLE_TYPE) + +struct BlockToSSBO { + int m_blockIndex; + Qt3DCore::QNodeId m_bufferID; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToSSBO, Q_PRIMITIVE_TYPE) + + +typedef QHash<int, UniformValue> PackUniformHash; + +class Q_AUTOTEST_EXPORT ShaderParameterPack +{ +public: + ~ShaderParameterPack(); + + void setUniform(const int glslNameId, const UniformValue &val); + void setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id); + void setUniformBuffer(BlockToUBO blockToUBO); + void setShaderStorageBuffer(BlockToSSBO blockToSSBO); + void setSubmissionUniform(const ShaderUniform &uniform); + + inline PackUniformHash &uniforms() { return m_uniforms; } + inline const PackUniformHash &uniforms() const { return m_uniforms; } + UniformValue uniform(const int glslNameId) const { return m_uniforms.value(glslNameId); } + + struct NamedTexture + { + NamedTexture() {} + NamedTexture(const int glslNameId, Qt3DCore::QNodeId texId, int uniformArrayIndex) + : glslNameId(glslNameId) + , texId(texId) + , uniformArrayIndex(uniformArrayIndex) + { } + + int glslNameId; + Qt3DCore::QNodeId texId; + int uniformArrayIndex; + }; + + inline QVector<NamedTexture> textures() const { return m_textures; } + inline QVector<BlockToUBO> uniformBuffers() const { return m_uniformBuffers; } + inline QVector<BlockToSSBO> shaderStorageBuffers() const { return m_shaderStorageBuffers; } + inline QVector<ShaderUniform> submissionUniforms() const { return m_submissionUniforms; } +private: + PackUniformHash m_uniforms; + + QVector<NamedTexture> m_textures; + QVector<BlockToUBO> m_uniformBuffers; + QVector<BlockToSSBO> m_shaderStorageBuffers; + QVector<ShaderUniform> m_submissionUniforms; + + friend class RenderView; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderParameterPack::NamedTexture, Q_PRIMITIVE_TYPE) + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Qt3DRender::Render::ShaderParameterPack) + +#endif // QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H diff --git a/src/render/renderers/opengl/renderer/shadervariables_p.h b/src/render/renderers/opengl/renderer/shadervariables_p.h new file mode 100644 index 000000000..e0fa07dff --- /dev/null +++ b/src/render/renderers/opengl/renderer/shadervariables_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_SHADERVARIABLES_P_H +#define QT3DRENDER_RENDER_SHADERVARIABLES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qt3drender_global.h> +#include <QOpenGLContext> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +struct ShaderAttribute +{ + ShaderAttribute() + : m_nameId(-1) + , m_type(0) + , m_size(0) + , m_location(-1) + {} + + QString m_name; + int m_nameId; + GLenum m_type; + int m_size; + int m_location; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderAttribute, Q_MOVABLE_TYPE) + +struct ShaderUniform +{ + ShaderUniform() + : m_nameId(-1) + , m_type(GL_NONE) + , m_size(0) + , m_offset(-1) + , m_location(-1) + , m_blockIndex(-1) + , m_arrayStride(-1) + , m_matrixStride(-1) + , m_rawByteSize(0) + {} + + QString m_name; + int m_nameId; + GLenum m_type; + int m_size; + int m_offset; // -1 default, >= 0 if uniform defined in uniform block + int m_location; // -1 if uniform defined in a uniform block + int m_blockIndex; // -1 is the default, >= 0 if uniform defined in uniform block + int m_arrayStride; // -1 is the default, >= 0 if uniform defined in uniform block and if it's an array + int m_matrixStride; // -1 is the default, >= 0 uniform defined in uniform block and is a matrix + uint m_rawByteSize; // contains byte size (size / type / strides) + // size, offset and strides are in bytes +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniform, Q_MOVABLE_TYPE) + +struct ShaderUniformBlock +{ + ShaderUniformBlock() + : m_nameId(-1) + , m_index(-1) + , m_binding(-1) + , m_activeUniformsCount(0) + , m_size(0) + {} + + QString m_name; + int m_nameId; + int m_index; + int m_binding; + int m_activeUniformsCount; + int m_size; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniformBlock, Q_MOVABLE_TYPE) + +struct ShaderStorageBlock +{ + ShaderStorageBlock() + : m_nameId(-1) + , m_index(-1) + , m_binding(-1) + , m_size(0) + , m_activeVariablesCount(0) + {} + + QString m_name; + int m_nameId; + int m_index; + int m_binding; + int m_size; + int m_activeVariablesCount; +}; +QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderStorageBlock, Q_MOVABLE_TYPE) + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_SHADERVARIABLES_P_H diff --git a/src/render/renderers/opengl/renderstates/renderstates.pri b/src/render/renderers/opengl/renderstates/renderstates.pri new file mode 100644 index 000000000..10f51a0ed --- /dev/null +++ b/src/render/renderers/opengl/renderstates/renderstates.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/renderstateset.cpp + +HEADERS += \ + $$PWD/renderstateset_p.h diff --git a/src/render/renderers/opengl/renderstates/renderstateset.cpp b/src/render/renderers/opengl/renderstates/renderstateset.cpp new file mode 100644 index 000000000..bf84b0e1c --- /dev/null +++ b/src/render/renderers/opengl/renderstates/renderstateset.cpp @@ -0,0 +1,393 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderstateset_p.h" + +#include <bitset> + +#include <QDebug> +#include <QOpenGLContext> + +#include <Qt3DRender/private/submissioncontext_p.h> +#include <Qt3DRender/private/renderstates_p.h> +#include <Qt3DRender/private/qrenderstate_p.h> + +#include <Qt3DRender/qalphacoverage.h> +#include <Qt3DRender/qalphatest.h> +#include <Qt3DRender/private/qalphatest_p.h> +#include <Qt3DRender/qblendequation.h> +#include <Qt3DRender/private/qblendequation_p.h> +#include <Qt3DRender/qblendequationarguments.h> +#include <Qt3DRender/private/qblendequationarguments_p.h> +#include <Qt3DRender/qcolormask.h> +#include <Qt3DRender/private/qcolormask_p.h> +#include <Qt3DRender/qcullface.h> +#include <Qt3DRender/private/qcullface_p.h> +#include <Qt3DRender/qnodepthmask.h> +#include <Qt3DRender/qdepthtest.h> +#include <Qt3DRender/private/qdepthtest_p.h> +#include <Qt3DRender/qdithering.h> +#include <Qt3DRender/qfrontface.h> +#include <Qt3DRender/private/qfrontface_p.h> +#include <Qt3DRender/qpointsize.h> +#include <Qt3DRender/private/qpointsize_p.h> +#include <Qt3DRender/qpolygonoffset.h> +#include <Qt3DRender/private/qpolygonoffset_p.h> +#include <Qt3DRender/qscissortest.h> +#include <Qt3DRender/private/qscissortest_p.h> +#include <Qt3DRender/qstenciltest.h> +#include <Qt3DRender/private/qstenciltest_p.h> +#include <Qt3DRender/qstenciltestarguments.h> +#include <Qt3DRender/private/qstenciltestarguments_p.h> +#include <Qt3DRender/qclipplane.h> +#include <Qt3DRender/private/qclipplane_p.h> +#include <Qt3DRender/qmultisampleantialiasing.h> +#include <Qt3DRender/qseamlesscubemap.h> +#include <Qt3DRender/qstenciloperation.h> +#include <Qt3DRender/private/qstenciloperation_p.h> +#include <Qt3DRender/qstenciloperationarguments.h> +#include <Qt3DRender/private/qstenciloperationarguments_p.h> +#include <Qt3DRender/qstencilmask.h> +#include <Qt3DRender/private/qstencilmask_p.h> +#include <Qt3DRender/qlinewidth.h> +#include <Qt3DRender/private/qlinewidth_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +RenderStateSet::RenderStateSet() + : m_stateMask(0) +{ +} + +RenderStateSet::~RenderStateSet() +{ +} + +template<> +void RenderStateSet::addState<StateVariant>(const StateVariant &ds) +{ + m_states.push_back(ds); + m_stateMask |= ds.type; +} + +int RenderStateSet::changeCost(RenderStateSet *previousState) +{ + if (previousState == this) + return 0; + + int cost = 0; + + // first, find cost of any resets + StateMaskSet invOurState = ~stateMask(); + StateMaskSet stateToReset = previousState->stateMask() & invOurState; + + std::bitset<64> bs(stateToReset); + cost += int(bs.count()); + + // now, find out how many states we're changing + for (const StateVariant &ds : qAsConst(m_states)) { + // if the other state contains matching, then doesn't + // contribute to cost at all + if (previousState->contains(ds)) + continue; + + // flat cost for now; could be replaced with a cost() method on + // RenderState + cost += 2; + } + + return cost; +} + +void RenderStateSet::apply(SubmissionContext *gc) +{ + RenderStateSet* previousStates = gc->currentStateSet(); + + const StateMaskSet invOurState = ~stateMask(); + // generate a mask for each set bit in previous, where we do not have + // the corresponding bit set. + + StateMaskSet stateToReset = 0; + if (previousStates) { + stateToReset = previousStates->stateMask() & invOurState; + qCDebug(RenderStates) << "previous states " << QString::number(previousStates->stateMask(), 2); + } + qCDebug(RenderStates) << " current states " << QString::number(stateMask(), 2) << "inverse " << QString::number(invOurState, 2) << " -> states to change: " << QString::number(stateToReset, 2); + + // Reset states that aren't active in the current state set + resetMasked(stateToReset, gc); + + // Apply states that weren't in the previous state or that have + // different values + for (const StateVariant &ds : qAsConst(m_states)) { + if (previousStates && previousStates->contains(ds)) + continue; + ds.apply(gc); + } +} + +StateMaskSet RenderStateSet::stateMask() const +{ + return m_stateMask; +} + +void RenderStateSet::merge(RenderStateSet *other) +{ + m_stateMask |= other->stateMask(); +} + +void RenderStateSet::resetMasked(StateMaskSet maskOfStatesToReset, SubmissionContext *gc) +{ + // TO DO -> Call gcHelper methods instead of raw GL + // QOpenGLFunctions shouldn't be used here directly + QOpenGLFunctions *funcs = gc->openGLContext()->functions(); + + if (maskOfStatesToReset & ScissorStateMask) + funcs->glDisable(GL_SCISSOR_TEST); + + if (maskOfStatesToReset & BlendStateMask) + funcs->glDisable(GL_BLEND); + + if (maskOfStatesToReset & StencilWriteStateMask) + funcs->glStencilMask(0); + + if (maskOfStatesToReset & StencilTestStateMask) + funcs->glDisable(GL_STENCIL_TEST); + + if (maskOfStatesToReset & DepthTestStateMask) + funcs->glDisable(GL_DEPTH_TEST); + + if (maskOfStatesToReset & DepthWriteStateMask) + funcs->glDepthMask(GL_TRUE); // reset to default + + if (maskOfStatesToReset & FrontFaceStateMask) + funcs->glFrontFace(GL_CCW); // reset to default + + if (maskOfStatesToReset & CullFaceStateMask) + funcs->glDisable(GL_CULL_FACE); + + if (maskOfStatesToReset & DitheringStateMask) + funcs->glDisable(GL_DITHER); + + if (maskOfStatesToReset & AlphaCoverageStateMask) + gc->setAlphaCoverageEnabled(false); + + if (maskOfStatesToReset & PointSizeMask) + gc->pointSize(false, 1.0f); // reset to default + + if (maskOfStatesToReset & PolygonOffsetStateMask) + funcs->glDisable(GL_POLYGON_OFFSET_FILL); + + if (maskOfStatesToReset & ColorStateMask) + funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + if (maskOfStatesToReset & ClipPlaneMask) { + GLint max = gc->maxClipPlaneCount(); + for (GLint i = 0; i < max; ++i) + gc->disableClipPlane(i); + } + + if (maskOfStatesToReset & SeamlessCubemapMask) + gc->setSeamlessCubemap(false); + + if (maskOfStatesToReset & StencilOpMask) + funcs->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + if (maskOfStatesToReset & LineWidthMask) + funcs->glLineWidth(1.0f); +} + +bool RenderStateSet::contains(const StateVariant &ds) const +{ + // trivial reject using the state mask bits + if (!(ds.type & stateMask())) + return false; + + for (const StateVariant &rs : m_states) { + if (rs == ds) + return true; + } + return false; +} + +StateVariant RenderStateSet::initializeStateFromPeer(const Qt3DRender::QRenderStateCreatedChangeBasePtr change) +{ + switch (change->renderStateType()) { + case AlphaCoverageStateMask: { + return RenderStateSet::createState<AlphaCoverage>(); + } + + case AlphaTestMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QAlphaTestData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<AlphaFunc>(data.alphaFunction, data.referenceValue); + } + + case BlendStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QBlendEquationData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<BlendEquation>(data.blendFunction); + } + + case BlendEquationArgumentsMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QBlendEquationArgumentsData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<BlendEquationArguments>( + data.sourceRgb, data.destinationRgb, + data.sourceAlpha, data.destinationAlpha, + change->isNodeEnabled(), + data.bufferIndex); + } + + case MSAAEnabledStateMask: { + return RenderStateSet::createState<MSAAEnabled>(change->isNodeEnabled()); + } + + case CullFaceStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QCullFaceData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<CullFace>(data.mode); + } + + case DepthWriteStateMask: { + return RenderStateSet::createState<NoDepthMask>(false); + } + + case DepthTestStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QDepthTestData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<DepthTest>(data.depthFunction); + } + + case FrontFaceStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QFrontFaceData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<FrontFace>(data.direction); + } + + case ScissorStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QScissorTestData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<ScissorTest>(data.left, data.bottom, + data.width, data.height); + } + + case StencilTestStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QStencilTestData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<StencilTest>(data.front.stencilFunction, + data.front.referenceValue, + data.front.comparisonMask, + data.back.stencilFunction, + data.back.referenceValue, + data.back.comparisonMask); + } + + case PointSizeMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QPointSizeData>>(change); + const auto &data = typedChange->data; + const bool isProgrammable = (data.sizeMode == QPointSize::Programmable); + return RenderStateSet::createState<PointSize>(isProgrammable, data.value); + } + + case PolygonOffsetStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QPolygonOffsetData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<PolygonOffset>(data.scaleFactor, data.depthSteps); + } + + case ColorStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QColorMaskData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<ColorMask>(data.redMasked, data.greenMasked, + data.blueMasked, data.alphaMasked); + } + + case ClipPlaneMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QClipPlaneData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<ClipPlane>(data.planeIndex, + data.normal, + data.distance); + } + + case SeamlessCubemapMask: { + return RenderStateSet::createState<SeamlessCubemap>(); + } + + case StencilOpMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QStencilOperationData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<StencilOp>(data.front.stencilTestFailureOperation, + data.front.depthTestFailureOperation, + data.front.allTestsPassOperation, + data.back.stencilTestFailureOperation, + data.back.depthTestFailureOperation, + data.back.allTestsPassOperation); + } + + case StencilWriteStateMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QStencilMaskData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<StencilMask>(data.frontOutputMask, + data.backOutputMask); + } + + case LineWidthMask: { + const auto typedChange = qSharedPointerCast<Qt3DRender::QRenderStateCreatedChange<QLineWidthData>>(change); + const auto &data = typedChange->data; + return RenderStateSet::createState<LineWidth>(data.value, data.smooth); + } + + // TODO: Fix Dithering state + case DitheringStateMask: + default: + Q_UNREACHABLE(); + return StateVariant(); + } +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/renderstates/renderstateset_p.h b/src/render/renderers/opengl/renderstates/renderstateset_p.h new file mode 100644 index 000000000..58d46c7a6 --- /dev/null +++ b/src/render/renderers/opengl/renderstates/renderstateset_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERSTATE_H +#define QT3DRENDER_RENDER_RENDERSTATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/genericstate_p.h> +#include <Qt3DRender/private/renderstates_p.h> +#include <Qt3DRender/private/statevariant_p.h> +#include <QVector> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +class QRenderState; + +namespace Render { + +class SubmissionContext; +class RenderState; + +class RenderStateSet +{ +public: + RenderStateSet(); + ~RenderStateSet(); + + template<typename GenericState> + void addState(const GenericState &state) + { + addState(StateVariant::fromValue(state)); + } + + /** + * @brief changeCost - metric of cost to change to this state-set from + * a candidate previous state-set. This is used to find an optimal + * ordering of state-sets when sending draw commands. + * @param previousState + * @return + */ + int changeCost(RenderStateSet* previousState); + + void apply(SubmissionContext *gc); + + StateMaskSet stateMask() const; + void merge(RenderStateSet *other); + void resetMasked(StateMaskSet maskOfStatesToReset, SubmissionContext* gc); + + template<class State, typename ... Args> + static StateVariant createState(Args... values) + { + State state; + state.set(values...); + return StateVariant::fromValue(state); + } + + static StateVariant initializeStateFromPeer(const Qt3DRender::QRenderStateCreatedChangeBasePtr change); + +private: + /** + * @brief contains - check if this set contains a matching piece of state + * @param ds + * @return + */ + bool contains(const StateVariant &ds) const; + + StateMaskSet m_stateMask; + QVector<StateVariant> m_states; +}; + +template<> +void RenderStateSet::addState<StateVariant>(const StateVariant &state); + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERSTATE_H diff --git a/src/render/renderers/opengl/textures/gltexture.cpp b/src/render/renderers/opengl/textures/gltexture.cpp new file mode 100644 index 000000000..e94122f67 --- /dev/null +++ b/src/render/renderers/opengl/textures/gltexture.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qhash.h> +#include "gltexture_p.h" + +#include <QDebug> +#include <QOpenGLFunctions> +#include <QOpenGLTexture> +#include <QOpenGLPixelTransferOptions> +#include <Qt3DRender/qtexture.h> +#include <Qt3DRender/qtexturedata.h> +#include <Qt3DRender/qtextureimagedata.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/texturedatamanager_p.h> +#include <Qt3DRender/private/qabstracttexture_p.h> +#include <Qt3DRender/private/renderbuffer_p.h> +#include <Qt3DCore/qpropertyupdatedchange.h> +#include <Qt3DCore/qpropertynodeaddedchange.h> +#include <Qt3DCore/qpropertynoderemovedchange.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt3DCore; + +namespace Qt3DRender { +namespace Render { + +GLTexture::GLTexture(TextureDataManager *texDataMgr, + TextureImageDataManager *texImgDataMgr, + const QTextureGeneratorPtr &texGen, + bool unique) + : m_unique(unique) + , m_gl(nullptr) + , m_renderBuffer(nullptr) + , m_textureDataManager(texDataMgr) + , m_textureImageDataManager(texImgDataMgr) + , m_dataFunctor(texGen) +{ + // make sure texture generator is executed + // this is needed when Texture have the TargetAutomatic + // to ensure they are loaded before trying to instantiate the QOpenGLTexture + if (!texGen.isNull()) + m_textureDataManager->requestData(texGen, this); +} + +GLTexture::~GLTexture() +{ + destroyGLTexture(); +} + +void GLTexture::destroyResources() +{ + if (m_dataFunctor) + m_textureDataManager->releaseData(m_dataFunctor, this); +} + +void GLTexture::destroyGLTexture() +{ + delete m_gl; + m_gl = nullptr; + delete m_renderBuffer; + m_renderBuffer = nullptr; + + m_dirtyFlags.store(0); + + destroyResources(); +} + +QOpenGLTexture* GLTexture::getOrCreateGLTexture() +{ + QMutexLocker locker(&m_textureMutex); + bool needUpload = false; + + // on the first invocation in the render thread, make sure to + // evaluate the texture data generator output + // (this might change some property values) + if (m_dataFunctor && !m_textureData) { + m_textureData = m_textureDataManager->getData(m_dataFunctor); + + // if there is a texture generator, most properties will be defined by it + if (m_textureData) { + if (m_properties.target != QAbstractTexture::TargetAutomatic) + qWarning() << "[Qt3DRender::GLTexture] When a texture provides a generator, it's target is expected to be TargetAutomatic"; + + m_actualTarget = m_textureData->target(); + m_properties.width = m_textureData->width(); + m_properties.height = m_textureData->height(); + m_properties.depth = m_textureData->depth(); + m_properties.layers = m_textureData->layers(); + m_properties.format = m_textureData->format(); + + const QVector<QTextureImageDataPtr> imageData = m_textureData->imageData(); + + if (imageData.size() > 0) { + // Set the mips level based on the first image if autoMipMapGeneration is disabled + if (!m_properties.generateMipMaps) + m_properties.mipLevels = imageData.first()->mipLevels(); + } + + setDirtyFlag(Properties, true); + needUpload = true; + } else { + qWarning() << "[Qt3DRender::GLTexture] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame"; + return nullptr; + } + } + + // additional texture images may be defined through image data generators + if (testDirtyFlag(TextureData)) { + m_imageData.clear(); + needUpload = true; + + int maxMipLevel = 0; + for (const Image &img : qAsConst(m_images)) { + const QTextureImageDataPtr imgData = m_textureImageDataManager->getData(img.generator); + + Q_ASSERT(imgData); + m_imageData.push_back(imgData); + maxMipLevel = qMax(maxMipLevel, img.mipLevel); + + // If the texture doesn't have a texture generator, we will + // derive some properties from the first TextureImage (layer=0, miplvl=0, face=0) + if (!m_textureData && img.layer == 0 && img.mipLevel == 0 && img.face == QAbstractTexture::CubeMapPositiveX) { + if (imgData->width() != -1 && imgData->height() != -1 && imgData->depth() != -1) { + m_properties.width = imgData->width(); + m_properties.height = imgData->height(); + m_properties.depth = imgData->depth(); + } + // Set the format of the texture if the texture format is set to Automatic + if (m_properties.format == QAbstractTexture::Automatic) { + m_properties.format = static_cast<QAbstractTexture::TextureFormat>(imgData->format()); + } + setDirtyFlag(Properties, true); + } + } + + // make sure the number of mip levels is set when there is no texture data generator + if (!m_dataFunctor) { + m_properties.mipLevels = maxMipLevel + 1; + setDirtyFlag(Properties, true); + } + } + + // don't try to create the texture if the format was not set + if (m_properties.format == QAbstractTexture::Automatic) + return nullptr; + + // if the properties changed, we need to re-allocate the texture + if (testDirtyFlag(Properties)) { + delete m_gl; + m_gl = nullptr; + } + + if (!m_gl) { + m_gl = buildGLTexture(); + if (!m_gl) + return nullptr; + m_gl->allocateStorage(); + if (!m_gl->isStorageAllocated()) { + return nullptr; + } + } + + // need to (re-)upload texture data? + if (needUpload) { + uploadGLTextureData(); + setDirtyFlag(TextureData, false); + } + + // need to set texture parameters? + if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) { + updateGLTextureParameters(); + } + + // un-set properties and parameters. The TextureData flag might have been set by another thread + // in the meantime, so don't clear that. + setDirtyFlag(Properties, false); + setDirtyFlag(Parameters, false); + + return m_gl; +} + +RenderBuffer *GLTexture::getOrCreateRenderBuffer() +{ + QMutexLocker locker(&m_textureMutex); + + if (m_dataFunctor && !m_textureData) { + m_textureData = m_textureDataManager->getData(m_dataFunctor); + if (m_textureData) { + if (m_properties.target != QAbstractTexture::TargetAutomatic) + qWarning() << "[Qt3DRender::GLTexture] [renderbuffer] When a texture provides a generator, it's target is expected to be TargetAutomatic"; + + m_properties.width = m_textureData->width(); + m_properties.height = m_textureData->height(); + m_properties.format = m_textureData->format(); + + setDirtyFlag(Properties); + } else { + qWarning() << "[Qt3DRender::GLTexture] [renderbuffer] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame"; + return nullptr; + } + } + + if (testDirtyFlag(Properties)) { + delete m_renderBuffer; + m_renderBuffer = nullptr; + } + + if (!m_renderBuffer) + m_renderBuffer = new RenderBuffer(m_properties.width, m_properties.height, m_properties.format); + + setDirtyFlag(Properties, false); + setDirtyFlag(Parameters, false); + + return m_renderBuffer; +} + +void GLTexture::setParameters(const TextureParameters ¶ms) +{ + QMutexLocker locker(&m_textureMutex); + if (m_parameters != params) { + m_parameters = params; + setDirtyFlag(Parameters); + } +} + +void GLTexture::setProperties(const TextureProperties &props) +{ + QMutexLocker locker(&m_textureMutex); + if (m_properties != props) { + m_properties = props; + m_actualTarget = props.target; + setDirtyFlag(Properties); + } +} + +void GLTexture::setImages(const QVector<Image> &images) +{ + // check if something has changed at all + bool same = (images.size() == m_images.size()); + if (same) { + for (int i = 0; i < images.size(); i++) { + if (images[i] != m_images[i]) { + same = false; + break; + } + } + } + + + if (!same) { + m_images = images; + requestUpload(); + } +} + +void GLTexture::setGenerator(const QTextureGeneratorPtr &generator) +{ + // Note: we do not compare if the generator is different + // as in some cases we may want to reset the same generator to force a reload + // e.g when using remote urls for textures + if (m_dataFunctor) + m_textureDataManager->releaseData(m_dataFunctor, this); + + m_textureData.reset(); + m_dataFunctor = generator; + + if (m_dataFunctor) { + m_textureDataManager->requestData(m_dataFunctor, this); + requestUpload(); + } +} + +// Return nullptr if +// - context cannot be obtained +// - texture hasn't yet been loaded +QOpenGLTexture *GLTexture::buildGLTexture() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning() << Q_FUNC_INFO << "requires an OpenGL context"; + return nullptr; + } + + if (m_actualTarget == QAbstractTexture::TargetAutomatic) { + // If the target is automatic at this point, it means that the texture + // hasn't been loaded yet (case of remote urls) and that loading failed + // and that target format couldn't be deduced + return nullptr; + } + + QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(m_actualTarget)); + + // m_format may not be ES2 compatible. Now it's time to convert it, if necessary. + QAbstractTexture::TextureFormat format = m_properties.format; + if (ctx->isOpenGLES() && ctx->format().majorVersion() < 3) { + switch (m_properties.format) { + case QOpenGLTexture::RGBA8_UNorm: + case QOpenGLTexture::RGBAFormat: + format = QAbstractTexture::RGBAFormat; + break; + case QOpenGLTexture::RGB8_UNorm: + case QOpenGLTexture::RGBFormat: + format = QAbstractTexture::RGBFormat; + break; + case QOpenGLTexture::DepthFormat: + format = QAbstractTexture::DepthFormat; + break; + default: + qWarning() << Q_FUNC_INFO << "could not find a matching OpenGL ES 2.0 unsized texture format"; + break; + } + } + + // Map ETC1 to ETC2 when supported. This allows using features like + // immutable storage as ETC2 is standard in GLES 3.0, while the ETC1 extension + // is written against GLES 1.0. + if (m_properties.format == QAbstractTexture::RGB8_ETC1) { + if ((ctx->isOpenGLES() && ctx->format().majorVersion() >= 3) + || ctx->hasExtension(QByteArrayLiteral("GL_OES_compressed_ETC2_RGB8_texture")) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_ES3_compatibility"))) + format = m_properties.format = QAbstractTexture::RGB8_ETC2; + } + + glTex->setFormat(m_properties.format == QAbstractTexture::Automatic ? + QOpenGLTexture::NoFormat : + static_cast<QOpenGLTexture::TextureFormat>(format)); + glTex->setSize(m_properties.width, m_properties.height, m_properties.depth); + // Set layers count if texture array + if (m_actualTarget == QAbstractTexture::Target1DArray || + m_actualTarget == QAbstractTexture::Target2DArray || + m_actualTarget == QAbstractTexture::Target3D || + m_actualTarget == QAbstractTexture::Target2DMultisampleArray || + m_actualTarget == QAbstractTexture::TargetCubeMapArray) { + glTex->setLayers(m_properties.layers); + } + + if (m_actualTarget == QAbstractTexture::Target2DMultisample || + m_actualTarget == QAbstractTexture::Target2DMultisampleArray) { + // Set samples count if multisampled texture + // (multisampled textures don't have mipmaps) + glTex->setSamples(m_properties.samples); + } else if (m_properties.generateMipMaps) { + glTex->setMipLevels(glTex->maximumMipLevels()); + } else { + glTex->setAutoMipMapGenerationEnabled(false); + glTex->setMipBaseLevel(0); + glTex->setMipMaxLevel(m_properties.mipLevels - 1); + glTex->setMipLevels(m_properties.mipLevels); + } + + if (!glTex->create()) { + qWarning() << Q_FUNC_INFO << "creating QOpenGLTexture failed"; + return nullptr; + } + + return glTex; +} + +static void uploadGLData(QOpenGLTexture *glTex, + int level, int layer, QOpenGLTexture::CubeMapFace face, + const QByteArray &bytes, const QTextureImageDataPtr &data) +{ + if (data->isCompressed()) { + glTex->setCompressedData(level, layer, face, bytes.size(), bytes.constData()); + } else { + QOpenGLPixelTransferOptions uploadOptions; + uploadOptions.setAlignment(1); + glTex->setData(level, layer, face, data->pixelFormat(), data->pixelType(), bytes.constData(), &uploadOptions); + } +} + +void GLTexture::uploadGLTextureData() +{ + // Upload all QTexImageData set by the QTextureGenerator + if (m_textureData) { + const QVector<QTextureImageDataPtr> imgData = m_textureData->imageData(); + + for (const QTextureImageDataPtr &data : imgData) { + const int mipLevels = m_properties.generateMipMaps ? 1 : data->mipLevels(); + + for (int layer = 0; layer < data->layers(); layer++) { + for (int face = 0; face < data->faces(); face++) { + for (int level = 0; level < mipLevels; level++) { + // ensure we don't accidentally cause a detach / copy of the raw bytes + const QByteArray bytes(data->data(layer, face, level)); + uploadGLData(m_gl, level, layer, + static_cast<QOpenGLTexture::CubeMapFace>(QOpenGLTexture::CubeMapPositiveX + face), + bytes, data); + } + } + } + } + } + + // Upload all QTexImageData references by the TextureImages + for (int i = 0; i < m_images.size(); i++) { + const QTextureImageDataPtr &imgData = m_imageData.at(i); + + // ensure we don't accidentally cause a detach / copy of the raw bytes + const QByteArray bytes(imgData->data()); + uploadGLData(m_gl, m_images[i].mipLevel, m_images[i].layer, + static_cast<QOpenGLTexture::CubeMapFace>(m_images[i].face), + bytes, imgData); + } +} + +void GLTexture::updateGLTextureParameters() +{ + m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX)); + if (m_actualTarget != QAbstractTexture::Target1D && + m_actualTarget != QAbstractTexture::Target1DArray && + m_actualTarget != QAbstractTexture::TargetBuffer) + m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY)); + if (m_actualTarget == QAbstractTexture::Target3D) + m_gl->setWrapMode(QOpenGLTexture::DirectionR, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ)); + m_gl->setMinMagFilters(static_cast<QOpenGLTexture::Filter>(m_parameters.minificationFilter), + static_cast<QOpenGLTexture::Filter>(m_parameters.magnificationFilter)); + if (m_gl->hasFeature(QOpenGLTexture::AnisotropicFiltering)) + m_gl->setMaximumAnisotropy(m_parameters.maximumAnisotropy); + if (m_gl->hasFeature(QOpenGLTexture::TextureComparisonOperators)) { + m_gl->setComparisonFunction(static_cast<QOpenGLTexture::ComparisonFunction>(m_parameters.comparisonFunction)); + m_gl->setComparisonMode(static_cast<QOpenGLTexture::ComparisonMode>(m_parameters.comparisonMode)); + } +} + + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/textures/gltexture_p.h b/src/render/renderers/opengl/textures/gltexture_p.h new file mode 100644 index 000000000..cde0a6973 --- /dev/null +++ b/src/render/renderers/opengl/textures/gltexture_p.h @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GLTEXTURE_H +#define QT3DRENDER_RENDER_GLTEXTURE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qtexture.h> +#include <Qt3DRender/qtextureimagedata.h> +#include <Qt3DRender/qtexturegenerator.h> +#include <Qt3DRender/private/backendnode_p.h> +#include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/texture_p.h> +#include <QOpenGLContext> +#include <QFlags> +#include <QMutex> +#include <QSize> + +QT_BEGIN_NAMESPACE + +class QOpenGLTexture; + +namespace Qt3DRender { +namespace Render { + +class TextureImageManager; +class TextureDataManager; +class TextureImageDataManager; +class RenderBuffer; + +/** + * @brief + * Actual implementation of the OpenGL texture object. Makes sure the + * QOpenGLTexture is up-to-date with the generators, properties and parameters + * that were set for this GLTexture. + * + * Can be shared among multiple QTexture backend nodes through the + * GLTextureManager, which will make sure that there are no two GLTextures + * sharing the same texture data. + * + * A GLTexture can be unique though. In that case, it will not be shared + * between QTextures, but private to one QTexture only. + * + * A GLTexture can also represent an OpenGL renderbuffer object. This is used + * only in certain special cases, mainly to provide a packed depth-stencil + * renderbuffer suitable as an FBO attachment with OpenGL ES 3.1 and earlier. + * Such a GLTexture will have no texture object under the hood, and therefore + * the only valid operation is getOrCreateRenderBuffer(). + */ +class Q_AUTOTEST_EXPORT GLTexture +{ +public: + GLTexture(TextureDataManager *texDataMgr, + TextureImageDataManager *texImgDataMgr, + const QTextureGeneratorPtr &texGen, + bool unique); + + ~GLTexture(); + + /** + * Helper class to hold the defining properties of TextureImages + */ + struct Image { + QTextureImageDataGeneratorPtr generator; + int layer; + int mipLevel; + QAbstractTexture::CubeMapFace face; + + inline bool operator==(const Image &o) const { + bool sameGenerators = (generator == o.generator) + || (!generator.isNull() && !o.generator.isNull() && *generator == *o.generator); + return sameGenerators && layer == o.layer && mipLevel == o.mipLevel && face == o.face; + } + inline bool operator!=(const Image &o) const { return !(*this == o); } + }; + + inline bool isUnique() const { return m_unique; } + + inline TextureProperties properties() const { return m_properties; } + inline TextureParameters parameters() const { return m_parameters; } + inline QTextureGeneratorPtr textureGenerator() const { return m_dataFunctor; } + inline QVector<Image> images() const { return m_images; } + + inline QSize size() const { return QSize(m_properties.width, m_properties.height); } + inline QOpenGLTexture *getGLTexture() const { return m_gl; } + + /** + * @brief + * Returns the QOpenGLTexture for this GLTexture. If necessary, + * the GL texture will be created from the TextureImageDatas associated + * with the texture and image functors. If no functors are provided, + * the texture will be created without images. + * + * If the texture properties or parameters have changed, these changes + * will be applied to the resulting OpenGL texture. + */ + QOpenGLTexture* getOrCreateGLTexture(); + + /** + * @brief + * Returns the RenderBuffer for this GLTexture. If this is the first + * call, the OpenGL renderbuffer object will be created. + */ + RenderBuffer *getOrCreateRenderBuffer(); + + /** + * @brief Make sure to call this before calling the dtor + */ + void destroyGLTexture(); + + // Called by TextureDataManager when it has new texture data from + // a generator that needs to be uploaded. + void requestUpload() + { + setDirtyFlag(TextureData, true); + } + + bool isDirty() + { + return m_dirtyFlags.load() != 0; + } + + QMutex *textureLock() + { + return &m_textureMutex; + } + +protected: + template<class APITexture, class APITextureImage> + friend class APITextureManager; + + /* + * These methods are to be accessed from the GLTextureManager. + * The renderer and the texture backend nodes can only modify Textures + * through the GLTextureManager. + * + * The methods should only be called for unique textures, or textures + * that are not shared between multiple nodes. + */ + void setParameters(const TextureParameters ¶ms); + void setProperties(const TextureProperties &props); + void setImages(const QVector<Image> &images); + void setGenerator(const QTextureGeneratorPtr &generator); + +private: + + enum DirtyFlag { + TextureData = 0x01, // one or more image generators have been executed, data needs uploading to GPU + Properties = 0x02, // texture needs to be (re-)created + Parameters = 0x04 // texture parameters need to be (re-)set + + }; + + bool testDirtyFlag(DirtyFlag flag) + { + return m_dirtyFlags.load() & flag; + } + + void setDirtyFlag(DirtyFlag flag, bool value = true) + { + if (value) + m_dirtyFlags |= flag; + else + m_dirtyFlags &= ~static_cast<int>(flag); + } + + QOpenGLTexture *buildGLTexture(); + void uploadGLTextureData(); + void updateGLTextureParameters(); + void destroyResources(); + + bool m_unique; + QAtomicInt m_dirtyFlags; + QMutex m_textureMutex; + QOpenGLTexture *m_gl; + RenderBuffer *m_renderBuffer; + + TextureDataManager *m_textureDataManager; + TextureImageDataManager *m_textureImageDataManager; + + // target which is actually used for GL texture + QAbstractTexture::Target m_actualTarget; + TextureProperties m_properties; + TextureParameters m_parameters; + + QTextureGeneratorPtr m_dataFunctor; + QVector<Image> m_images; + + // cache actual image data generated by the functors + QTextureDataPtr m_textureData; + QVector<QTextureImageDataPtr> m_imageData; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLTEXTURE_H diff --git a/src/render/renderers/opengl/textures/gltexturemanager_p.h b/src/render/renderers/opengl/textures/gltexturemanager_p.h new file mode 100644 index 000000000..1c8b49911 --- /dev/null +++ b/src/render/renderers/opengl/textures/gltexturemanager_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_GLTEXTUREMANAGER_H +#define QT3DRENDER_RENDER_GLTEXTUREMANAGER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/apitexturemanager_p.h> +#include <Qt3DRender/private/gltexture_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class Q_AUTOTEST_EXPORT GLTextureManager : public APITextureManager<GLTexture, GLTexture::Image> +{ +public: + explicit GLTextureManager(TextureImageManager *textureImageManager, + TextureDataManager *textureDataManager, + TextureImageDataManager *textureImageDataManager) + : APITextureManager<GLTexture, GLTexture::Image>(textureImageManager, + textureDataManager, + textureImageDataManager) + {} +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_GLTEXTUREMANAGER_H diff --git a/src/render/renderers/opengl/textures/renderbuffer.cpp b/src/render/renderers/opengl/textures/renderbuffer.cpp new file mode 100644 index 000000000..bc5050f73 --- /dev/null +++ b/src/render/renderers/opengl/textures/renderbuffer.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderbuffer_p.h" +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFunctions> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +RenderBuffer::RenderBuffer(int width, int height, QAbstractTexture::TextureFormat format) + : m_size(width, height), + m_format(format), + m_renderBuffer(0), + m_context(nullptr) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning("Renderbuffer requires an OpenGL context"); + return; + } + + m_context = ctx; + QOpenGLFunctions *f = ctx->functions(); + f->glGenRenderbuffers(1, &m_renderBuffer); + if (!m_renderBuffer) + return; + + f->glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer); + while (f->glGetError() != GL_NO_ERROR) { } + f->glRenderbufferStorage(GL_RENDERBUFFER, format, width, height); + GLint err = f->glGetError(); + if (err != GL_NO_ERROR) + qWarning("Failed to set renderbuffer storage: error 0x%x", err); + f->glBindRenderbuffer(GL_RENDERBUFFER, 0); +} + +RenderBuffer::~RenderBuffer() +{ + if (m_renderBuffer) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + // Ignore the fact that renderbuffers are sharable resources and let's + // just expect that the context is the same as when the resource was + // created. QOpenGLTexture suffers from the same limitation anyway, and + // this is unlikely to become an issue within Qt 3D. + if (ctx == m_context) { + ctx->functions()->glDeleteRenderbuffers(1, &m_renderBuffer); + } else { + qWarning("Wrong current context; renderbuffer not destroyed"); + } + } +} + +void RenderBuffer::bind() +{ + if (!m_renderBuffer) + return; + + m_context->functions()->glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer); +} + +void RenderBuffer::release() +{ + if (!m_context) + return; + + m_context->functions()->glBindRenderbuffer(GL_RENDERBUFFER, 0); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/textures/renderbuffer_p.h b/src/render/renderers/opengl/textures/renderbuffer_p.h new file mode 100644 index 000000000..7dc62492a --- /dev/null +++ b/src/render/renderers/opengl/textures/renderbuffer_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RENDERBUFFER_P_H +#define QT3DRENDER_RENDER_RENDERBUFFER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/qabstracttexture.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; + +namespace Qt3DRender { +namespace Render { + +class Q_AUTOTEST_EXPORT RenderBuffer +{ +public: + RenderBuffer(int width, int height, QAbstractTexture::TextureFormat format); + ~RenderBuffer(); + + int width() const { return m_size.width(); } + int height() const { return m_size.height(); } + QSize size() const { return m_size; } + QAbstractTexture::TextureFormat format() const { return m_format; } + GLuint renderBufferId() const { return m_renderBuffer; } + + void bind(); + void release(); + +private: + QSize m_size; + QAbstractTexture::TextureFormat m_format; + GLuint m_renderBuffer; + QOpenGLContext *m_context; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERBUFFER_P_H diff --git a/src/render/renderers/opengl/textures/textures.pri b/src/render/renderers/opengl/textures/textures.pri new file mode 100644 index 000000000..42ffff7eb --- /dev/null +++ b/src/render/renderers/opengl/textures/textures.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/gltexture.cpp \ + $$PWD/renderbuffer.cpp + +HEADERS += \ + $$PWD/gltexturemanager_p.h \ + $$PWD/gltexture_p.h \ + $$PWD/renderbuffer_p.h + + diff --git a/src/render/renderers/renderers.pri b/src/render/renderers/renderers.pri new file mode 100644 index 000000000..795a6fbdf --- /dev/null +++ b/src/render/renderers/renderers.pri @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +# OpenGL renderer +include($$OUT_PWD/qt3drender-config.pri) +QT_FOR_CONFIG += 3drender-private + +qtConfig(qt3d-opengl-renderer): include(opengl/opengl.pri) |