/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of Qt 3D Studio. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software 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 General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) 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.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-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "dragonactivatedsurface_p.h" // Backend utils #include // Backend types #include #include #include #include #include #include // GL types #include #include #include #if !defined(QT_OPENGL_ES_2) #include #include #include //#include #include #include #include //#include #endif #include #include #include // Qt3DRender::Render #include #include //#include #include #include #include #include #include #ifndef GL_ARRAY_BUFFER # define GL_ARRAY_BUFFER 0x8892 #endif #ifndef GL_UNIFORM_BUFFER # define GL_UNIFORM_BUFFER 0x8A11 #endif #ifndef GL_ELEMENT_ARRAY_BUFFER # define GL_ELEMENT_ARRAY_BUFFER 0x8893 #endif #ifndef GL_SHADER_STORAGE_BUFFER # define GL_SHADER_STORAGE_BUFFER 0x90D2 #endif #ifndef GL_PIXEL_PACK_BUFFER # define GL_PIXEL_PACK_BUFFER 0x88EB #endif #ifndef GL_PIXEL_UNPACK_BUFFER # define GL_PIXEL_UNPACK_BUFFER 0x88EC #endif #ifndef GL_DRAW_INDIRECT_BUFFER # define GL_DRAW_INDIRECT_BUFFER 0x8F3F #endif QT_BEGIN_NAMESPACE using namespace Qt3DCore; namespace Qt3DRender { using GraphicsHelperInterface = Dragon::GraphicsHelperInterface; namespace Dragon { 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}; GLint 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; } 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(); } } GLuint 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; } } // namespace ActivatedSurface::ActivatedSurface(QSurface *surface, QOpenGLContext *glContext, Render::SurfaceLocker *lock) : m_surface(surface) , m_glContext(glContext) { Q_UNUSED(lock) Q_ASSERT(lock->isSurfaceValid()); m_valid = m_glContext->makeCurrent(surface); if (!m_valid) return; if (m_glContext->format().majorVersion() >= 3) { m_supportsVAO = true; } else { QSet extensions = m_glContext->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_glContext->defaultFramebufferObject(); //qCDebug(Render::Backend) << "VAO support = " << m_supportsVAO; // TODO we might not want to do this repeatedly, put here now because we need them in multiple places m_glHelper = resolveHighestOpenGLFunctions(); // TODO reconsider resettings this on every frame resetMasked(0xFFFFFFFFFFFF); } ActivatedSurface::~ActivatedSurface() { if (!m_valid) return; // TODO should be cached between frames instead of released here... for (GLuint frameBufferId : m_renderTargets.values()) { m_glHelper->releaseFrameBufferObject(frameBufferId); } m_glContext->doneCurrent(); } bool ActivatedSurface::isValid() { return m_valid; } void ActivatedSurface::setGlHelper(QSharedPointer helper) { m_glHelper = helper; } void ActivatedSurface::clearBackBuffer(const ClearBackBufferInfo &info) { auto clearBufferTypes = info.buffers; if (clearBufferTypes & QClearBuffers::ColorBuffer) { const QVector4D vCol = info.color; const QColor color = QColor::fromRgbF(vCol.x(), vCol.y(), vCol.z(), vCol.w()); if (m_clearColor != color) { m_clearColor = color; m_glContext->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } } if (clearBufferTypes & QClearBuffers::DepthBuffer) { const float depth = info.depth; if (m_clearDepth != depth) { m_glContext->functions()->glClearDepthf(depth); } } if (clearBufferTypes & QClearBuffers::StencilBuffer) { const int stencil = info.stencil; if (m_clearStencil != stencil) { m_glContext->functions()->glClearStencil(stencil); } } if (clearBufferTypes != QClearBuffers::None) { GLbitfield mask = 0; if (clearBufferTypes & QClearBuffers::ColorBuffer) mask |= GL_COLOR_BUFFER_BIT; if (clearBufferTypes & QClearBuffers::DepthBuffer) mask |= GL_DEPTH_BUFFER_BIT; if (clearBufferTypes & QClearBuffers::StencilBuffer) mask |= GL_STENCIL_BUFFER_BIT; m_glContext->functions()->glClear(mask); } } bool ActivatedSurface::activateShader(const Immutable &shader) { if (shader->dna == m_activeShaderDNA) { return true; } // TODO consider m_material // // Ensure material uniforms are re-applied // m_material = nullptr; // TODO do we need to store the active shader? // m_activeShader = shader; shader->shaderProgram->bind(); m_activeShaderDNA = shader->dna; return true; } void ActivatedSurface::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() == UniformType::Int) { float value = float(*v.constData()); UniformValue floatV(value); applyUniformHelper(description, floatV); } else { applyUniformHelper(description, v); } break; case UniformType::Vec2: applyUniformHelper(description, v); break; case UniformType::Vec3: applyUniformHelper(description, v); break; case UniformType::Vec4: applyUniformHelper(description, v); break; case UniformType::Double: applyUniformHelper(description, v); break; case UniformType::DVec2: applyUniformHelper(description, v); break; case UniformType::DVec3: applyUniformHelper(description, v); break; case UniformType::DVec4: applyUniformHelper(description, v); break; case UniformType::Sampler: case UniformType::Int: applyUniformHelper(description, v); break; case UniformType::IVec2: applyUniformHelper(description, v); break; case UniformType::IVec3: applyUniformHelper(description, v); break; case UniformType::IVec4: applyUniformHelper(description, v); break; case UniformType::UInt: applyUniformHelper(description, v); break; case UniformType::UIVec2: applyUniformHelper(description, v); break; case UniformType::UIVec3: applyUniformHelper(description, v); break; case UniformType::UIVec4: applyUniformHelper(description, v); break; case UniformType::Bool: applyUniformHelper(description, v); break; case UniformType::BVec2: applyUniformHelper(description, v); break; case UniformType::BVec3: applyUniformHelper(description, v); break; case UniformType::BVec4: applyUniformHelper(description, v); break; case UniformType::Mat2: applyUniformHelper(description, v); break; case UniformType::Mat3: applyUniformHelper(description, v); break; case UniformType::Mat4: applyUniformHelper(description, v); break; case UniformType::Mat2x3: applyUniformHelper(description, v); break; case UniformType::Mat3x2: applyUniformHelper(description, v); break; case UniformType::Mat2x4: applyUniformHelper(description, v); break; case UniformType::Mat4x2: applyUniformHelper(description, v); break; case UniformType::Mat3x4: applyUniformHelper(description, v); break; case UniformType::Mat4x3: applyUniformHelper(description, v); break; default: break; } } DrawContext ActivatedSurface::beginDrawing(bool autoSwapBuffers) { return DrawContext(m_glContext, m_surface, autoSwapBuffers); } UniformValue standardUniformValue(StandardUniform standardUniformType, const Immutable &renderView, const Immutable &cameraMatrices, const Matrix4x4 &model) { auto getProjectionMatrix = [](const Immutable lens) { return lens->projection(); }; auto 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()); }; switch (standardUniformType) { case ModelMatrix: return UniformValue(model); case ViewMatrix: return UniformValue(cameraMatrices->viewMatrix); case ProjectionMatrix: return UniformValue(getProjectionMatrix(renderView->cameraLens)); case ModelViewMatrix: return UniformValue(cameraMatrices->viewMatrix * model); case ViewProjectionMatrix: return UniformValue(getProjectionMatrix(renderView->cameraLens) * cameraMatrices->viewMatrix); case ModelViewProjectionMatrix: return UniformValue(cameraMatrices->viewProjectionMatrix * model); case InverseModelMatrix: return UniformValue(model.inverted()); case InverseViewMatrix: return UniformValue(cameraMatrices->viewMatrix.inverted()); case InverseProjectionMatrix: { return UniformValue(getProjectionMatrix(renderView->cameraLens).inverted()); } case InverseModelViewMatrix: return UniformValue((cameraMatrices->viewMatrix * model).inverted()); case InverseViewProjectionMatrix: { const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(renderView->cameraLens) * cameraMatrices->viewMatrix; return UniformValue(viewProjectionMatrix.inverted()); } case InverseModelViewProjectionMatrix: return UniformValue((cameraMatrices->viewProjectionMatrix * model).inverted()); case ModelNormalMatrix: return UniformValue(convertToQMatrix4x4(model).normalMatrix()); case ModelViewNormalMatrix: return UniformValue(convertToQMatrix4x4(cameraMatrices->viewMatrix * model).normalMatrix()); case ViewportMatrix: { QMatrix4x4 viewportMatrix; // TO DO: Implement on Matrix4x4 viewportMatrix.viewport(resolveViewport(renderView->viewport, renderView->surfaceSize)); return UniformValue(Matrix4x4(viewportMatrix)); } case InverseViewportMatrix: { QMatrix4x4 viewportMatrix; // TO DO: Implement on Matrix4x4 viewportMatrix.viewport(resolveViewport(renderView->viewport, renderView->surfaceSize)); return UniformValue(Matrix4x4(viewportMatrix.inverted())); } case AspectRatio: return float(renderView->surfaceSize.width()) / float(renderView->surfaceSize.height()); case Exposure: return UniformValue(renderView->cameraLens->exposure()); case Gamma: return UniformValue(renderView->gamma); case Time: // TODO add back time // return UniformValue(float(m_renderer->time() / 1000000000.0f)); return UniformValue(0.0f); case EyePosition: return UniformValue(cameraMatrices->eyePosition); case SkinningPalette: { // const Armature *armature = entity->renderComponent(); // if (!armature) { // qCWarning(Jobs, "Requesting skinningPalette uniform but no armature set on entity"); // return UniformValue(); // } // return armature->skinningPaletteUniform(); qWarning() << "WARNING: Armatures not supported in Dragon renderer"; return UniformValue(); } default: Q_UNREACHABLE(); return UniformValue(); } } bool ActivatedSurface::bindParameters(const Immutable &renderView, const Immutable &cameraMatrices, const MutableContainer &glBuffers, const MutableContainer &glTextures, int maxTextureImageUnits, const Immutable &glShader, const Immutable &worldTransform, const ParameterNameToParameterMap ¶meterMap) { // ParameterMap = nameId -> parameter for current command // Set standard uniforms // TODO Once we have shader graphs, we should put standard uniforms in a uniform buffer object // and emulate UBOs on hardware that doesn't support it, including unrolling them in the // shaders for (const auto &standardShaderUniform : glShader->standardUniforms) { const auto &shaderUniform = standardShaderUniform.shaderUniform; const auto &setter = standardShaderUniform.standardUniform; UniformValue uniformValue = standardUniformValue(setter, renderView, cameraMatrices, *worldTransform); applyUniform(shaderUniform, uniformValue); } // Keep track of the currently bound texture unit int unit = 0; // TODO: In OpenGL 4.4+, there is bindTextures(), which we could add to our graphics helpers: // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindTextures.xhtml for (const auto &shaderUniform : glShader->uniforms) { // scalars and textures // TODO consider returning the result of this in cases where the parameters have not changed, // so that we can reuse the same parameter and avoid looking it up every frame. The returned // result can be stored in a map in the Frame const auto ¶meterIt = parameterMap.constFind(shaderUniform.m_nameId); if (parameterIt == parameterMap.cend()) continue; const auto ¶meter = parameterIt.value(); const auto &uniformValue = parameter->uniformValue(); if (uniformValue.valueType() != UniformValue::NodeId) { applyUniform(shaderUniform, uniformValue); continue; } // 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 const QNodeId *nodeIds = uniformValue.constData(); const int uniformArraySize = uint(uniformValue.byteSize()) / sizeof(QNodeId); for (int i = 0; i < uniformArraySize; ++i) { const QNodeId texId = nodeIds[i]; if (texId.isNull()) { qDebug() << "Got null texId, skipping"; // TODO this entire implementation is a bit fragile. Consider making UniformValue // store data in a more type-safe way for QNodeIds continue; } Q_ASSERT(glTextures.contains(texId)); const auto &texture = glTextures[texId]; // Q_ASSERT(texture->openGLTexture != nullptr); if (texture->openGLTexture == nullptr) { // TODO current valid case for this is when the format is automatic, however, // we need to investigate where this is used and why we should skip at this point continue; } ShaderParameterPack::NamedTexture namedTex(shaderUniform.m_nameId, texId, i); UniformValue textureValue(uniformArraySize * sizeof(int), UniformValue::TextureValue); std::fill(textureValue.data(), textureValue.data() + uniformArraySize, -1); if (unit >= maxTextureImageUnits) { // TODO: Check this earlier and skip rendering completely if this is the case. qWarning() << "WARNING: Max number of texture units exhausted," << "GL_MAX_TEXTURE_IMAGE_UNITS:" << maxTextureImageUnits << "Texture cannot be bound."; continue; } // NOTE: Qt3D used to have some logic to keep track of bound textures to avoid // re-binding. However, this introduced additional complexity and caused performance // problems on the CPU side. We should consider adding back some of this logic // if it turns out to be beneficial for performance. However, considering that // glBindTextures no longer uses glActiveTexture, it seems likely that there is // no need for this. texture->openGLTexture->bind(GLuint(unit)); textureValue.data()[namedTex.uniformArrayIndex] = unit; applyUniform(shaderUniform, textureValue); unit += 1; } } GLuint uboIndex = 0; for (const auto &shaderUniformBlock : glShader->uniformBlocks) { const auto ¶meterIt = parameterMap.constFind(shaderUniformBlock.m_nameId); if (parameterIt == parameterMap.cend()) continue; const auto ¶meter = parameterIt.value(); const auto &uniformValue = parameter->uniformValue(); if (uniformValue.valueType() != UniformValue::NodeId) { // TODO might be a bit spammy to send this to the output qWarning() << "WARNING: Found UniformBlock with corresponding parameter that was not of UniformValue::NodeId type. This is not supported"; } const auto &bufferId = *uniformValue.constData(); Q_ASSERT(glBuffers.contains(bufferId)); const auto &glBuffer = glBuffers[bufferId]; // TODO always zero, consider using other locations... // TODO Make sure that there's enough binding points const GLuint blockIndex = GLuint(shaderUniformBlock.m_index); const GLuint programId = glShader->shaderProgram->programId(); m_glHelper->bindUniformBlock(programId, blockIndex, uboIndex); // Needed to avoid conflict where the buffer would already be bound as a VertexArray const auto glType = glBufferTypes[GLBuffer::UniformBuffer]; m_glContext->functions()->glBindBuffer(glType, glBuffer->bufferId()); m_glHelper->bindBufferBase(glType, uboIndex++, glBuffer->bufferId()); // Buffer update to GL buffer will be done at render time } for (const auto &shaderStorageBlock : glShader->storageBlocks) { // TODO add support for shader storage blocks Q_UNUSED(shaderStorageBlock) qWarning() << "WARNING: Shader storage blocks are not yet supported"; } // if not all data is valid, the next frame will be rendered immediately return true; } bool ActivatedSurface::bindVertexArrayObject(const Immutable &vao) { if (m_supportsVAO) { Q_ASSERT(!vao->m_vao.isNull()); Q_ASSERT(vao->m_vao->isCreated()); vao->m_vao->bind(); return true; } else { qDebug() << "WARNING: Emulated VAO not yet supported"; return false; // // 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); } } Immutable ActivatedSurface::createVertexArrayObject( VAOIdentifier key, const Mutable &uploadedShader, const Immutable &geometry, const ValueContainer &attributes, const MutableContainer &uploadedBuffers) { GLVertexArrayObject vao; if (m_supportsVAO) { vao.m_vao.reset(new QOpenGLVertexArrayObject); vao.m_vao->create(); bindVertexArrayObject(vao); } vao.m_owners = key; // TODO actually check if geometry is dirty // TODO verify that the shader does not need to be bound to create a vertex array object // activateShader(uploadedShader); // TODO return bound vao for other commands to use or just move internals here const auto attributeIds = geometry->attributes(); for (QNodeId attributeId : attributeIds) { Q_ASSERT(attributes.contains(attributeId)); const auto &attribute = attributes[attributeId]; const auto &glBuffer = uploadedBuffers[attribute->bufferId()]; const auto attributeType = attributeTypeToGLBufferType(attribute->attributeType()); // TODO get GlBuffer // Index Attribute bool attributeWasDirty = attributes.dirtyOrNew().contains(attributeId); // TODO fix this by checking in which cases we need a real update bool geometryDirtyOrSomething = true; if (!attributeWasDirty && !geometryDirtyOrSomething) continue; if (attribute->attributeType() == QAttribute::IndexAttribute) { GLenum glType = glBufferTypes[attributeType]; m_glContext->functions()->glBindBuffer(glType, glBuffer->bufferId()); vao.m_indexAttribute = glBuffer; continue; } if (attribute->attributeType() != QAttribute::VertexAttribute) { qWarning() << "WARNING: Indirect attributes are not yet implemented in the Dragon renderer."; continue; } // Find the location for the attribute const QVector shaderAttributes = uploadedShader->attributes; Render::ShaderAttribute attributeDescription; bool foundDescription = false; for (const auto &shaderAttribute : shaderAttributes) { if (shaderAttribute.m_nameId == attribute->nameId()) { foundDescription = true; attributeDescription = shaderAttribute; break; } } // Strictly not necessary to check both, location is -1 by default if (!foundDescription || attributeDescription.m_location < 0) continue; const int location = attributeDescription.m_location; const GLint attributeDataType = glDataTypeFromAttributeDataType(attribute->vertexBaseType()); uint typeSize = 0; uint 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(); } GLVertexArrayObject::VAOVertexAttribute attr; // attr.bufferHandle = glBuffer; attr.attributeType = attributeType; attr.dataType = attributeDataType; attr.divisor = attribute->divisor(); attr.vertexSize = attribute->vertexSize() / attrCount; attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize); attr.shaderDataType = attributeDescription.m_type; for (uint i = 0; i < attrCount; i++) { // TODO consider making location uint attr.location = location + int(i); attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize); GLenum glType = glBufferTypes[attributeType]; m_glContext->functions()->glBindBuffer(glType, glBuffer->bufferId()); // 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(qintptr(attr.byteOffset))); // Done by the helper if it supports it if (attr.divisor != 0) m_glHelper->vertexAttribDivisor(attr.location, attr.divisor); // Save this in the current emulated VAO vao.saveVertexAttribute(attr); } } // TODO do this in a cleaner way? vao.m_vao->release(); return vao; } void ActivatedSurface::memoryBarrier(const QMemoryBarrier::Operations &operations) { if (operations != QMemoryBarrier::None) m_glHelper->memoryBarrier(operations); } // TODO Review (make more functional?) void ActivatedSurface::activateRenderTarget(GLuint fboId, const AttachmentPack &attachments) { // GLuint fboId = lastBoundFBOId; // we will revert to this if we did not get an FBO id // if (renderTargetNodeId) { // // New RenderTarget // if (!m_renderTargets.contains(renderTargetNodeId)) { // if (m_defaultFBO != 0 && 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, glTextures); // } // } else { // fboId = updateRenderTarget(renderTargetNodeId, attachments, true, glTextures); // } // } m_activeFBO = fboId; m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw); // Set active drawBuffers activateDrawBuffers(attachments); } // TODO Review (make more functional?) void ActivatedSurface::activateDrawBuffers(const AttachmentPack &attachments) { const QVector activeDrawBuffers = attachments.drawBuffers; if (!m_glHelper->checkFrameBufferComplete()) { qWarning() << "WARNING: FBO incomplete"; return; } if (activeDrawBuffers.size() <= 1) return; // We need MRT if the number is more than 1 if (!m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) { qWarning() << "WARNING: Active draw buffers > 1, but MRT is not supported."; return; } // Set up MRT, glDrawBuffers... m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data()); } bool ActivatedSurface::bindGLBuffer(const Mutable &buffer, GLBuffer::Type type) { // TODO DO NOT USE // TODO only bind in functions that need it, and immediately release when finished calling Q_UNUSED(buffer) Q_UNUSED(type) return true; } // TODO Review (make more functional?) GLuint ActivatedSurface::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, const MutableContainer &glTextures) { 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, glTextures); } else { qCritical("Failed to create FBO"); } return fboId; } // TODO Review (make more functional?) GLuint ActivatedSurface::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget, const MutableContainer &glTextures) { Q_ASSERT(m_renderTargets.contains(renderTargetNodeId)); 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? const QSize s = m_renderTargetsSize[fboId]; const auto attachments_ = attachments.outputs; for (const auto &attachment : attachments_) { // ### TODO QTBUG-64757 this check is insufficient since the // texture may have changed to another one with the same size. That // case is not handled atm. if (!glTextures.contains(attachment->textureUuid)) { qWarning() << "Could not find texture attachment" << attachment->textureUuid; continue; } const auto &glTexture = glTextures[attachment->textureUuid]; needsResize |= (glTexture->size() != s); if (isActiveRenderTarget) { if (attachment->point == QRenderTargetOutput::Color0) m_renderTargetFormat = glTexture->properties.format; } } } if (needsResize) { m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); bindFrameBufferAttachmentHelper(fboId, attachments, glTextures); } return fboId; } void ActivatedSurface::viewport(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()) { qWarning() << "WARNING: Got empty viewport size. Skipping."; 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_glContext->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()); } // TODO Review (make more functional?) QSize ActivatedSurface::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments, const MutableContainer &glTextures) { // 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; const auto attachments_ = attachments.outputs; for (const auto &output : attachments_) { if (!glTextures.contains(output->textureUuid)) { qWarning() << Q_FUNC_INFO << "WARNING: Could not find texture attachment" << output->textureUuid; continue; } const auto &glTexture = glTextures[output->textureUuid]; // TODO HACK workaround to avoid copying helpers to Dragon just yet Dragon::Attachment attachment; attachment.m_name = output->name; attachment.m_mipLevel = output->mipLevel; attachment.m_layer = output->layer; attachment.m_textureUuid = output->textureUuid; attachment.m_point = output->point; attachment.m_face = output->face; // END HACK if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) { QOpenGLTexture *glTex = glTexture->openGLTexture.data(); 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 { // TODO implement qWarning() << Q_FUNC_INFO << "WARNING: Not implemented"; // RenderBuffer *renderBuffer = glTexture ? // glTexture->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); // } } } // TODO hack, return from this function and use it properly in renderTargetSize m_renderTargetsSize.insert(fboId, fboSize); return fboSize; } // TODO review QSize ActivatedSurface::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_glContext->functions() ->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &attachmentObjectType); m_glContext->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(m_surface)->devicePixelRatio(); renderTargetSize *= dpr; } } return renderTargetSize; } GraphicsApiFilterData ActivatedSurface::contextInfo() { GraphicsApiFilterData result; QStringList extensions; const auto exts = m_glContext->extensions(); for (const QByteArray &ext : exts) extensions << QString::fromUtf8(ext); result.m_major = m_glContext->format().version().first; result.m_minor = m_glContext->format().version().second; result.m_api = m_glContext->isOpenGLES() ? QGraphicsApiFilter::OpenGLES : QGraphicsApiFilter::OpenGL; result.m_profile = static_cast( m_glContext->format().profile()); result.m_extensions = extensions; result.m_vendor = QString::fromUtf8( reinterpret_cast(m_glContext->functions()->glGetString(GL_VENDOR))); return result; } QSharedPointer ActivatedSurface::resolveHighestOpenGLFunctions() const { Q_ASSERT(m_glContext); QSharedPointer glHelper; if (m_glContext->isOpenGLES()) { if (m_glContext->format().majorVersion() >= 3) { if (m_glContext->format().minorVersion() >= 2) { glHelper.reset(new Dragon::GraphicsHelperES3_2()); //qCDebug(Render::Backend) << Q_FUNC_INFO << " Building OpenGL ES 3.2 Helper"; } else { glHelper.reset(new Dragon::GraphicsHelperES3()); //qCDebug(Render::Backend) << Q_FUNC_INFO << " Building OpenGL ES 3.0 Helper"; } } else { glHelper.reset(new Dragon::GraphicsHelperES2()); //qCDebug(Render::Backend) << Q_FUNC_INFO << " Building OpenGL ES2 Helper"; } glHelper->initializeHelper(m_glContext, nullptr); } #ifndef QT_OPENGL_ES_2 else { QAbstractOpenGLFunctions *glFunctions = nullptr; // TODO clean up /* if ((glFunctions = m_glContext->versionFunctions()) != nullptr) { qCDebug(Render::Backend) << Q_FUNC_INFO << " Building OpenGL 4.3"; glHelper.reset(new Dragon::GraphicsHelperGL4()); } else*/ if ((glFunctions = m_glContext->versionFunctions()) != nullptr) { //qCDebug(Render::Backend) << Q_FUNC_INFO << " Building OpenGL 3.3"; glHelper.reset(new Dragon::GraphicsHelperGL3_3()); } else if ((glFunctions = m_glContext->versionFunctions()) != nullptr) { //qCDebug(Render::Backend) << Q_FUNC_INFO << " Building OpenGL 3.2"; glHelper.reset(new Dragon::GraphicsHelperGL3_2()); } else if ((glFunctions = m_glContext->versionFunctions()) != nullptr) { //qCDebug(Render::Backend) << Q_FUNC_INFO << " Building OpenGL 2 Helper"; glHelper.reset(new Dragon::GraphicsHelperGL2()); } Q_ASSERT_X(glHelper, "GraphicsContext::resolveHighestOpenGLFunctions", "unable to create valid helper for available OpenGL version"); glHelper->initializeHelper(m_glContext, glFunctions); } #endif // Set Vendor and Extensions of reference GraphicsApiFilter // TO DO: would that vary like the glHelper ? return glHelper; } namespace { // Render States Helpers template void applyStateHelper(const GenericState *state, ActivatedSurface *gc) { Q_UNUSED(state) Q_UNUSED(gc) } template<> void applyStateHelper(const AlphaFunc *state, ActivatedSurface *gc) { const auto values = state->values(); gc->glHelper()->alphaTest(std::get<0>(values), std::get<1>(values)); } template<> void applyStateHelper(const BlendEquationArguments *state, ActivatedSurface *gc) { const auto values = state->values(); // Un-indexed BlendEquationArguments -> Use normal GL1.0 functions if (std::get<5>(values) < 0) { if (std::get<4>(values)) { gc->openGLContext()->functions()->glEnable(GL_BLEND); gc->openGLContext()->functions()->glBlendFuncSeparate(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } else { gc->openGLContext()->functions()->glDisable(GL_BLEND); } } // BlendEquationArguments for a particular Draw Buffer. Different behaviours for // (1) 3.0-3.3: only enablei/disablei supported. // (2) 4.0+: all operations supported. // We just ignore blend func parameter for (1), so no warnings get // printed. else { if (std::get<4>(values)) { gc->glHelper()->enablei(GL_BLEND, std::get<5>(values)); if (gc->glHelper()->supportsFeature(GraphicsHelperInterface::DrawBuffersBlend)) { gc->glHelper()->blendFuncSeparatei(std::get<5>(values), std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } } else { gc->glHelper()->disablei(GL_BLEND, std::get<5>(values)); } } } template<> void applyStateHelper(const BlendEquation *state, ActivatedSurface *gc) { gc->glHelper()->blendEquation(std::get<0>(state->values())); } template<> void applyStateHelper(const MSAAEnabled *state, ActivatedSurface *gc) { gc->glHelper()->setMSAAEnabled(std::get<0>(state->values())); } template<> void applyStateHelper(const DepthTest *state, ActivatedSurface *gc) { gc->glHelper()->depthTest(std::get<0>(state->values())); } template<> void applyStateHelper(const NoDepthMask *state, ActivatedSurface *gc) { gc->glHelper()->depthMask(std::get<0>(state->values())); } template<> void applyStateHelper(const CullFace *state, ActivatedSurface *gc) { const auto values = state->values(); if (std::get<0>(values) == QCullFace::NoCulling) { gc->openGLContext()->functions()->glDisable(GL_CULL_FACE); } else { gc->openGLContext()->functions()->glEnable(GL_CULL_FACE); gc->openGLContext()->functions()->glCullFace(std::get<0>(values)); } } template<> void applyStateHelper(const FrontFace *state, ActivatedSurface *gc) { gc->glHelper()->frontFace(std::get<0>(state->values())); } template<> void applyStateHelper(const ScissorTest *state, ActivatedSurface *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glEnable(GL_SCISSOR_TEST); gc->openGLContext()->functions()->glScissor(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } template<> void applyStateHelper(const StencilTest *state, ActivatedSurface *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glEnable(GL_STENCIL_TEST); gc->openGLContext()->functions()->glStencilFuncSeparate(GL_FRONT, std::get<0>(values), std::get<1>(values), std::get<2>(values)); gc->openGLContext()->functions()->glStencilFuncSeparate(GL_BACK, std::get<3>(values), std::get<4>(values), std::get<5>(values)); } template<> void applyStateHelper(const AlphaCoverage *, ActivatedSurface *gc) { gc->glHelper()->setAlphaCoverageEnabled(true); } template<> void applyStateHelper(const PointSize *state, ActivatedSurface *gc) { const auto values = state->values(); gc->glHelper()->pointSize(std::get<0>(values), std::get<1>(values)); } template<> void applyStateHelper(const PolygonOffset *state, ActivatedSurface *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glEnable(GL_POLYGON_OFFSET_FILL); gc->openGLContext()->functions()->glPolygonOffset(std::get<0>(values), std::get<1>(values)); } template<> void applyStateHelper(const ColorMask *state, ActivatedSurface *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glColorMask(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } template<> void applyStateHelper(const ClipPlane *state, ActivatedSurface *gc) { const auto values = state->values(); gc->glHelper()->enableClipPlane(std::get<0>(values)); gc->glHelper()->setClipPlane(std::get<0>(values), std::get<1>(values), std::get<2>(values)); } template<> void applyStateHelper(const SeamlessCubemap *, ActivatedSurface *gc) { gc->glHelper()->setSeamlessCubemap(true); } template<> void applyStateHelper(const StencilOp *state, ActivatedSurface *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glStencilOpSeparate(GL_FRONT, std::get<0>(values), std::get<1>(values), std::get<2>(values)); gc->openGLContext()->functions()->glStencilOpSeparate(GL_BACK, std::get<3>(values), std::get<4>(values), std::get<5>(values)); } template<> void applyStateHelper(const StencilMask *state, ActivatedSurface *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glStencilMaskSeparate(GL_FRONT, std::get<0>(values)); gc->openGLContext()->functions()->glStencilMaskSeparate(GL_BACK, std::get<1>(values)); } template<> void applyStateHelper(const Dithering *, ActivatedSurface *gc) { gc->openGLContext()->functions()->glEnable(GL_DITHER); } #ifndef GL_LINE_SMOOTH #define GL_LINE_SMOOTH 0x0B20 #endif template<> void applyStateHelper(const LineWidth *state, ActivatedSurface *gc) { const auto values = state->values(); if (std::get<1>(values)) gc->openGLContext()->functions()->glEnable(GL_LINE_SMOOTH); else gc->openGLContext()->functions()->glDisable(GL_LINE_SMOOTH); gc->openGLContext()->functions()->glLineWidth(std::get<0>(values)); } } // namespace QOpenGLContext *ActivatedSurface::openGLContext() const { return m_glContext; } void ActivatedSurface::setCurrentStateSet(const Immutable &ss) { // if (ss->stateMask() == m_currentStateSet->stateMask()) // return; applyStateSet(ss); m_currentStateSet = ss; } void ActivatedSurface::applyState(const StateVariant &stateVariant) { switch (stateVariant.type) { case AlphaCoverageStateMask: { applyStateHelper(static_cast( stateVariant.constState()), this); break; } case AlphaTestMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case BlendStateMask: { applyStateHelper(static_cast( stateVariant.constState()), this); break; } case BlendEquationArgumentsMask: { applyStateHelper(static_cast( stateVariant.constState()), this); break; } case MSAAEnabledStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case CullFaceStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case DepthWriteStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case DepthTestStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case FrontFaceStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case ScissorStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case StencilTestStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case PointSizeMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case PolygonOffsetStateMask: { applyStateHelper(static_cast( stateVariant.constState()), this); break; } case ColorStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case ClipPlaneMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case SeamlessCubemapMask: { applyStateHelper(static_cast( stateVariant.constState()), this); break; } case StencilOpMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case StencilWriteStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case DitheringStateMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } case LineWidthMask: { applyStateHelper(static_cast(stateVariant.constState()), this); break; } default: Q_UNREACHABLE(); } } void ActivatedSurface::resetMasked(qint64 maskOfStatesToReset) { // TO DO -> Call gcHelper methods instead of raw GL // QOpenGLFunctions shouldn't be used here directly QOpenGLFunctions *funcs = m_glContext->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) m_glHelper->setAlphaCoverageEnabled(false); if (maskOfStatesToReset & PointSizeMask) m_glHelper->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 = m_glHelper->maxClipPlaneCount(); for (GLint i = 0; i < max; ++i) m_glHelper->disableClipPlane(i); } if (maskOfStatesToReset & SeamlessCubemapMask) m_glHelper->setSeamlessCubemap(false); if (maskOfStatesToReset & StencilOpMask) funcs->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); if (maskOfStatesToReset & LineWidthMask) funcs->glLineWidth(1.0f); } void ActivatedSurface::applyStateSet(Immutable ss) { // const auto &previousStates = m_currentStateSet; // const StateMaskSet invOurState = ~ss->stateMask(); // generate a mask for each set bit in previous, where we do not have // the corresponding bit set. // StateMaskSet stateToReset = 0; // stateToReset = previousStates->stateMask() & invOurState; // qCDebug(Render::RenderStates) << "previous states " // << QString::number(previousStates->stateMask(), 2); // qCDebug(Render::RenderStates) << " current states " << QString::number(ss->stateMask(), 2) // << "inverse " << QString::number(invOurState, 2) // << " -> states to change: " << QString::number(stateToReset, 2); // TODO Reset states that aren't active in the current state set // TODO I don't trust that this currently checks the values of the states, // hence, let's reset all just to make sure for now // resetMasked(stateToReset); resetMasked(0xFFFFFFFFFFFF); // Apply states that weren't in the previous state or that have // different values const QVector statesToSet = ss->states(); for (const StateVariant &ds : statesToSet) { // if (previousStates->contains(ds)) // continue; applyState(ds); } } // TODO consider moving to Commands void ActivatedSurface::blitFramebuffer(const BlitFramebufferInfo &blitFramebufferInfo, uint defaultFboId, const MutableContainer &glTextures) { const auto &inputTarget = blitFramebufferInfo.sourceRenderTarget; const auto &inputAttachments = blitFramebufferInfo.sourceAttachments; const auto &outputTarget = blitFramebufferInfo.destinationRenderTarget; const auto &outputAttachments = blitFramebufferInfo.destinationAttachments; const auto &inputRect = blitFramebufferInfo.node->sourceRect(); const auto &outputRect = blitFramebufferInfo.node->destinationRect(); const auto &inputAttachmentPoint = blitFramebufferInfo.node->sourceAttachmentPoint(); const auto &outputAttachmentPoint = blitFramebufferInfo.node->destinationAttachmentPoint(); const auto &interpolationMethod = blitFramebufferInfo.node->interpolationMethod(); GLuint inputFboId = defaultFboId; bool inputBufferIsDefault = true; if (inputTarget.has_value()) { const auto &inputRenderTarget = inputTarget.get(); if (m_renderTargets.contains(inputRenderTarget->peerId())) inputFboId = updateRenderTarget(inputRenderTarget->peerId(), inputAttachments.get(), false, glTextures); else inputFboId = createRenderTarget(inputRenderTarget->peerId(), inputAttachments.get(), glTextures); inputBufferIsDefault = false; } GLuint outputFboId = defaultFboId; bool outputBufferIsDefault = true; if (outputTarget.has_value()) { const auto &outputRenderTarget = outputTarget.get(); if (m_renderTargets.contains(outputRenderTarget->peerId())) outputFboId = updateRenderTarget(outputRenderTarget->peerId(), outputAttachments.get(), false, glTextures); else outputFboId = createRenderTarget(outputRenderTarget->peerId(), outputAttachments.get(), glTextures); 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 // TODO verify that this is the correct ID const GLuint lastDrawFboId = defaultFboId; // Activate input framebuffer for reading m_glHelper->bindFrameBufferObject(inputFboId, GraphicsHelperInterface::FBORead); // Activate output framebuffer for writing m_glHelper->bindFrameBufferObject(outputFboId, GraphicsHelperInterface::FBODraw); //Bind texture if (!inputBufferIsDefault) m_glHelper->readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint); if (!outputBufferIsDefault) { // Note that we use glDrawBuffers, not glDrawBuffer. The // latter is not available with GLES. const int buf = outputAttachmentPoint; m_glHelper->drawBuffers(1, &buf); } // Blit framebuffer const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR; m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, GL_COLOR_BUFFER_BIT, mode); // Reset draw buffer m_glHelper->bindFrameBufferObject(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw); if (outputAttachmentPoint != QRenderTargetOutput::Color0) { const int buf = QRenderTargetOutput::Color0; m_glHelper->drawBuffers(1, &buf); } } DrawContext::DrawContext(QOpenGLContext *openGLContext, QSurface *surface, bool autoSwapBuffers) : m_openGLContext(openGLContext) , m_surface(surface) , m_autoSwapBuffers(autoSwapBuffers) { } DrawContext::~DrawContext() { if (m_autoSwapBuffers) m_openGLContext->swapBuffers(m_surface); } #define QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformTypeEnum, BaseType, Func) \ template<> \ void ActivatedSurface::applyUniformHelper(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()); \ } QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Float, float, glUniform1fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Vec2, float, glUniform2fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Vec3, float, glUniform3fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Vec4, float, glUniform4fv) // OpenGL expects int* as values for booleans QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Bool, int, glUniform1iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::BVec2, int, glUniform2iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::BVec3, int, glUniform3iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::BVec4, int, glUniform4iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Int, int, glUniform1iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::IVec2, int, glUniform2iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::IVec3, int, glUniform3iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::IVec4, int, glUniform4iv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::UInt, uint, glUniform1uiv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::UIVec2, uint, glUniform2uiv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::UIVec3, uint, glUniform3uiv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::UIVec4, uint, glUniform4uiv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat2, float, glUniformMatrix2fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat3, float, glUniformMatrix3fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat4, float, glUniformMatrix4fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat2x3, float, glUniformMatrix2x3fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat3x2, float, glUniformMatrix3x2fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat2x4, float, glUniformMatrix2x4fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat4x2, float, glUniformMatrix4x2fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat3x4, float, glUniformMatrix3x4fv) QT3D_DRAGON_UNIFORM_TYPE_IMPL(UniformType::Mat4x3, float, glUniformMatrix4x3fv) } // namespace Dragon } // namespace Qt3DRender QT_END_NAMESPACE