/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(QT_OPENGL_ES_2) #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #include 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 { namespace Rhi { static QHash static_contexts; unsigned int nextFreeContextId() { for (unsigned int i=0; i < 0xffff; ++i) { if (!static_contexts.contains(i)) return i; } qFatal("Couldn't find free context ID"); return 0; } namespace { RHIBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type) { switch (type) { case QAttribute::VertexAttribute: return RHIBuffer::ArrayBuffer; case QAttribute::IndexAttribute: return RHIBuffer::IndexBuffer; case QAttribute::DrawIndirectAttribute: return RHIBuffer::DrawIndirectBuffer; default: Q_UNREACHABLE(); } } void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width, uint height, QAbstractTexture::TextureFormat format) { switch (format) { case QAbstractTexture::RGBA32F: { uchar *srcScanline = (uchar *)srcData + stride * (height - 1); for (uint i = 0; i < height; ++i) { uchar *dstScanline = img.scanLine(i); float *pSrc = (float*)srcScanline; for (uint j = 0; j < width; j++) { *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+2], 1.0f)); *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+1], 1.0f)); *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+0], 1.0f)); *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4*j+3], 1.0f)); } srcScanline -= stride; } } break; default: { uchar* srcScanline = (uchar *)srcData + stride * (height - 1); for (uint i = 0; i < height; ++i) { memcpy(img.scanLine(i), srcScanline, stride); srcScanline -= stride; } } break; } } // Render States Helpers /* template void applyStateHelper(const GenericState *state, SubmissionContext *gc) { Q_UNUSED(state); Q_UNUSED(gc); } template<> void applyStateHelper(const AlphaFunc *state, SubmissionContext *gc) { const auto values = state->values(); gc->alphaTest(std::get<0>(values), std::get<1>(values)); } template<> void applyStateHelper(const BlendEquationArguments *state, SubmissionContext *gc) { const auto values = state->values(); // Un-indexed BlendEquationArguments -> Use normal GL1.0 functions if (std::get<5>(values) < 0) { if (std::get<4>(values)) { gc->openGLContext()->functions()->glEnable(GL_BLEND); gc->openGLContext()->functions()->glBlendFuncSeparate(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } else { gc->openGLContext()->functions()->glDisable(GL_BLEND); } } // BlendEquationArguments for a particular Draw Buffer. Different behaviours for // (1) 3.0-3.3: only enablei/disablei supported. // (2) 4.0+: all operations supported. // We just ignore blend func parameter for (1), so no warnings get // printed. else { if (std::get<4>(values)) { gc->enablei(GL_BLEND, std::get<5>(values)); if (gc->supportsDrawBuffersBlend()) { gc->blendFuncSeparatei(std::get<5>(values), std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } } else { gc->disablei(GL_BLEND, std::get<5>(values)); } } } template<> void applyStateHelper(const BlendEquation *state, SubmissionContext *gc) { gc->blendEquation(std::get<0>(state->values())); } template<> void applyStateHelper(const MSAAEnabled *state, SubmissionContext *gc) { gc->setMSAAEnabled(std::get<0>(state->values())); } template<> void applyStateHelper(const DepthRange *state, SubmissionContext *gc) { const auto values = state->values(); gc->depthRange(std::get<0>(values), std::get<1>(values)); } template<> void applyStateHelper(const DepthTest *state, SubmissionContext *gc) { gc->depthTest(std::get<0>(state->values())); } template<> void applyStateHelper(const RasterMode *state, SubmissionContext *gc) { gc->rasterMode(std::get<0>(state->values()), std::get<1>(state->values())); } template<> void applyStateHelper(const NoDepthMask *state, SubmissionContext *gc) { gc->depthMask(std::get<0>(state->values())); } template<> void applyStateHelper(const CullFace *state, SubmissionContext *gc) { const auto values = state->values(); if (std::get<0>(values) == QCullFace::NoCulling) { gc->openGLContext()->functions()->glDisable(GL_CULL_FACE); } else { gc->openGLContext()->functions()->glEnable(GL_CULL_FACE); gc->openGLContext()->functions()->glCullFace(std::get<0>(values)); } } template<> void applyStateHelper(const FrontFace *state, SubmissionContext *gc) { gc->frontFace(std::get<0>(state->values())); } template<> void applyStateHelper(const ScissorTest *state, SubmissionContext *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glEnable(GL_SCISSOR_TEST); gc->openGLContext()->functions()->glScissor(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } template<> void applyStateHelper(const StencilTest *state, SubmissionContext *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glEnable(GL_STENCIL_TEST); gc->openGLContext()->functions()->glStencilFuncSeparate(GL_FRONT, std::get<0>(values), std::get<1>(values), std::get<2>(values)); gc->openGLContext()->functions()->glStencilFuncSeparate(GL_BACK, std::get<3>(values), std::get<4>(values), std::get<5>(values)); } template<> void applyStateHelper(const AlphaCoverage *, SubmissionContext *gc) { gc->setAlphaCoverageEnabled(true); } template<> void applyStateHelper(const PointSize *state, SubmissionContext *gc) { const auto values = state->values(); gc->pointSize(std::get<0>(values), std::get<1>(values)); } template<> void applyStateHelper(const PolygonOffset *state, SubmissionContext *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glEnable(GL_POLYGON_OFFSET_FILL); gc->openGLContext()->functions()->glPolygonOffset(std::get<0>(values), std::get<1>(values)); } template<> void applyStateHelper(const ColorMask *state, SubmissionContext *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glColorMask(std::get<0>(values), std::get<1>(values), std::get<2>(values), std::get<3>(values)); } template<> void applyStateHelper(const ClipPlane *state, SubmissionContext *gc) { const auto values = state->values(); gc->enableClipPlane(std::get<0>(values)); gc->setClipPlane(std::get<0>(values), std::get<1>(values), std::get<2>(values)); } template<> void applyStateHelper(const SeamlessCubemap *, SubmissionContext *gc) { gc->setSeamlessCubemap(true); } template<> void applyStateHelper(const StencilOp *state, SubmissionContext *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glStencilOpSeparate(GL_FRONT, std::get<0>(values), std::get<1>(values), std::get<2>(values)); gc->openGLContext()->functions()->glStencilOpSeparate(GL_BACK, std::get<3>(values), std::get<4>(values), std::get<5>(values)); } template<> void applyStateHelper(const StencilMask *state, SubmissionContext *gc) { const auto values = state->values(); gc->openGLContext()->functions()->glStencilMaskSeparate(GL_FRONT, std::get<0>(values)); gc->openGLContext()->functions()->glStencilMaskSeparate(GL_BACK, std::get<1>(values)); } template<> void applyStateHelper(const Dithering *, SubmissionContext *gc) { gc->openGLContext()->functions()->glEnable(GL_DITHER); } #ifndef GL_LINE_SMOOTH #define GL_LINE_SMOOTH 0x0B20 #endif template<> void applyStateHelper(const LineWidth *state, SubmissionContext *gc) { const auto values = state->values(); if (std::get<1>(values)) gc->openGLContext()->functions()->glEnable(GL_LINE_SMOOTH); else gc->openGLContext()->functions()->glDisable(GL_LINE_SMOOTH); gc->openGLContext()->functions()->glLineWidth(std::get<0>(values)); } */ } // anonymous SubmissionContext::SubmissionContext() : GraphicsContext() , m_ownCurrent(true) , m_id(nextFreeContextId()) , m_surface(nullptr) , m_activeShader(nullptr) , m_renderTargetFormat(QAbstractTexture::NoFormat) , m_currClearStencilValue(0) , m_currClearDepthValue(1.f) , m_currClearColorValue(0,0,0,0) , m_material(nullptr) , m_activeFBO(0) , m_stateSet(nullptr) , m_renderer(nullptr) , m_uboTempArray(QByteArray(1024, 0)) { static_contexts[m_id] = this; } SubmissionContext::~SubmissionContext() { releaseOpenGL(); Q_ASSERT(static_contexts[m_id] == this); static_contexts.remove(m_id); } void SubmissionContext::initialize() { GraphicsContext::initialize(); m_textureContext.initialize(this); m_imageContext.initialize(this); } void SubmissionContext::resolveRenderTargetFormat() { RHI_UNIMPLEMENTED; //* const QSurfaceFormat format = m_gl->format(); //* const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize(); //* const uint r = format.redBufferSize(); //* const uint g = format.greenBufferSize(); //* const uint b = format.blueBufferSize(); //* //* #define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18)) //* //* const uint bits = RGBA_BITS(r,g,b,a); //* switch (bits) { //* case RGBA_BITS(8,8,8,8): //* m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm; //* break; //* case RGBA_BITS(8,8,8,0): //* m_renderTargetFormat = QAbstractTexture::RGB8_UNorm; //* break; //* case RGBA_BITS(5,6,5,0): //* m_renderTargetFormat = QAbstractTexture::R5G6B5; //* break; //* } //* #undef RGBA_BITS } bool SubmissionContext::beginDrawing(QSurface *surface) { Q_ASSERT(surface); //* 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(m_surface)->isExposed()) // return false; // qDebug() << Q_FUNC_INFO << 2; // } // Makes the surface current on the OpenGLContext // and sets the right glHelper // m_ownCurrent = !(m_gl->surface() == m_surface); // if (m_ownCurrent && !makeCurrent(m_surface)) // return false; // TODO: cache surface format somewhere rather than doing this every time render surface changes resolveRenderTargetFormat(); #if defined(QT3D_RENDER_ASPECT_RHI_DEBUG) GLint err = m_gl->functions()->glGetError(); if (err != 0) { qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err; } #endif if (!isInitialized()) initialize(); // initializeHelpers(m_surface); // need to reset these values every frame, may get overwritten elsewhere RHI_UNIMPLEMENTED; //* 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_rhi->beginFrame(m_sc); return true; } void SubmissionContext::endDrawing(bool swapBuffers) { m_rhi->endFrame(m_sc, {}); RHI_UNIMPLEMENTED; //* if (swapBuffers) //* m_gl->swapBuffers(m_surface); //* if (m_ownCurrent) //* m_gl->doneCurrent(); m_textureContext.endDrawing(); m_imageContext.endDrawing(); //* static int i = 0; //* if(i++ == 10) //* std::exit(0); } void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId) { RHI_UNIMPLEMENTED; GLuint fboId = defaultFboId; // Default FBO if (renderTargetNodeId) { // New RenderTarget if (!m_renderTargets.contains(renderTargetNodeId)) { if (m_defaultFBO && fboId == m_defaultFBO) { // this is the default fbo that some platforms create (iOS), we just register it // Insert FBO into hash m_renderTargets.insert(renderTargetNodeId, fboId); } else { RHI_UNIMPLEMENTED; fboId = createRenderTarget(renderTargetNodeId, attachments); } } else { RHI_UNIMPLEMENTED; fboId = updateRenderTarget(renderTargetNodeId, attachments, true); } } m_activeFBO = fboId; //* m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw); // Set active drawBuffers activateDrawBuffers(attachments); } GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments) { RHI_UNIMPLEMENTED; return 0; //* const GLuint fboId = m_glHelper->createFrameBufferObject(); //* if (fboId) { //* // The FBO is created and its attachments are set once //* // Insert FBO into hash //* m_renderTargets.insert(renderTargetNodeId, fboId); //* // Bind FBO //* m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); //* bindFrameBufferAttachmentHelper(fboId, attachments); //* } else { //* qCritical("Failed to create FBO"); //* } //* return fboId; } GLuint SubmissionContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget) { RHI_UNIMPLEMENTED; return 0; //* const GLuint fboId = m_renderTargets.value(renderTargetNodeId); //* //* // We need to check if one of the attachment was resized //* bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet? //* if (!needsResize) { //* // render target exists, has attachment been resized? //* RHITextureManager *rhiTextureManager = m_renderer->rhiResourceManagers()->rhiTextureManager(); //* const QSize s = m_renderTargetsSize[fboId]; //* const auto attachments_ = attachments.attachments(); //* for (const Attachment &attachment : attachments_) { //* RHITexture *rTex = rhiTextureManager->lookupResource(attachment.m_textureUuid); //* // ### TODO QTBUG-64757 this check is insufficient since the //* // texture may have changed to another one with the same size. That //* // case is not handled atm. //* if (rTex) { //* needsResize |= rTex->size() != s; //* if (isActiveRenderTarget && attachment.m_point == QRenderTargetOutput::Color0) //* m_renderTargetFormat = rTex->properties().format; //* } //* } //* } //* //* if (needsResize) { //* m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); //* bindFrameBufferAttachmentHelper(fboId, attachments); //* } //* //* return fboId; } QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const { RHI_UNIMPLEMENTED; return surfaceSize; //* QSize renderTargetSize{}; //* if (m_activeFBO != m_defaultFBO) { //* // For external FBOs we may not have a m_renderTargets entry. //* if (m_renderTargetsSize.contains(m_activeFBO)) { //* renderTargetSize = m_renderTargetsSize[m_activeFBO]; //* } else if (surfaceSize.isValid()) { //* renderTargetSize = surfaceSize; //* } else { //* // External FBO (when used with QtQuick2 Scene3D) //* //* // Query FBO color attachment 0 size //* GLint attachmentObjectType = GL_NONE; //* GLint attachment0Name = 0; //* m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, //* GL_COLOR_ATTACHMENT0, //* GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, //* &attachmentObjectType); //* m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, //* GL_COLOR_ATTACHMENT0, //* GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, //* &attachment0Name); //* //* if (attachmentObjectType == GL_RENDERBUFFER && m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval)) //* renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name); //* else if (attachmentObjectType == GL_TEXTURE && m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval)) //* // Assumes texture level 0 and GL_TEXTURE_2D target //* renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name, GL_TEXTURE_2D); //* else //* return renderTargetSize; //* } //* } else { //* renderTargetSize = m_surface->size(); //* if (m_surface->surfaceClass() == QSurface::Window) { //* const float dpr = static_cast(m_surface)->devicePixelRatio(); //* renderTargetSize *= dpr; //* } //* } //* return renderTargetSize; } QImage SubmissionContext::readFramebuffer(const QRect &rect) { RHI_UNIMPLEMENTED; return {}; //* QImage img; //* const unsigned int area = rect.width() * rect.height(); //* unsigned int bytes; //* GLenum format, type; //* QImage::Format imageFormat; //* uint stride; //* //* /* format value should match GL internalFormat */ //* GLenum internalFormat = m_renderTargetFormat; //* //* switch (m_renderTargetFormat) { //* case QAbstractTexture::RGBAFormat: //* case QAbstractTexture::RGBA8_SNorm: //* case QAbstractTexture::RGBA8_UNorm: //* case QAbstractTexture::RGBA8U: //* case QAbstractTexture::SRGB8_Alpha8: //*#ifdef QT_OPENGL_ES_2 //* format = GL_RGBA; //* imageFormat = QImage::Format_RGBA8888_Premultiplied; //*#else //* format = GL_BGRA; //* imageFormat = QImage::Format_ARGB32_Premultiplied; //* internalFormat = GL_RGBA8; //*#endif //* type = GL_UNSIGNED_BYTE; //* bytes = area * 4; //* stride = rect.width() * 4; //* break; //* case QAbstractTexture::SRGB8: //* case QAbstractTexture::RGBFormat: //* case QAbstractTexture::RGB8U: //* case QAbstractTexture::RGB8_UNorm: //*#ifdef QT_OPENGL_ES_2 //* format = GL_RGBA; //* imageFormat = QImage::Format_RGBX8888; //*#else //* format = GL_BGRA; //* imageFormat = QImage::Format_RGB32; //* internalFormat = GL_RGB8; //*#endif //* type = GL_UNSIGNED_BYTE; //* bytes = area * 4; //* stride = rect.width() * 4; //* break; //*#ifndef QT_OPENGL_ES_2 //* case QAbstractTexture::RG11B10F: //* bytes = area * 4; //* format = GL_RGB; //* type = GL_UNSIGNED_INT_10F_11F_11F_REV; //* imageFormat = QImage::Format_RGB30; //* stride = rect.width() * 4; //* break; //* case QAbstractTexture::RGB10A2: //* bytes = area * 4; //* format = GL_RGBA; //* type = GL_UNSIGNED_INT_2_10_10_10_REV; //* imageFormat = QImage::Format_A2BGR30_Premultiplied; //* stride = rect.width() * 4; //* break; //* case QAbstractTexture::R5G6B5: //* bytes = area * 2; //* format = GL_RGB; //* type = GL_UNSIGNED_SHORT; //* internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV; //* imageFormat = QImage::Format_RGB16; //* stride = rect.width() * 2; //* break; //* case QAbstractTexture::RGBA16F: //* case QAbstractTexture::RGBA16U: //* case QAbstractTexture::RGBA32F: //* case QAbstractTexture::RGBA32U: //* bytes = area * 16; //* format = GL_RGBA; //* type = GL_FLOAT; //* imageFormat = QImage::Format_ARGB32_Premultiplied; //* stride = rect.width() * 16; //* break; //*#endif //* default: //* auto warning = qWarning(); //* warning << "Unable to convert"; //* QtDebugUtils::formatQEnum(warning, m_renderTargetFormat); //* warning << "render target texture format to QImage."; //* return img; //* } //* //* GLint samples = 0; //* m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples); //* if (samples > 0 && !m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) { //* qCWarning(Backend) << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; " //* "Required feature BlitFramebuffer is missing."; //* return img; //* } //* //* img = QImage(rect.width(), rect.height(), imageFormat); //* //* QScopedArrayPointer data(new uchar [bytes]); //* //* if (samples > 0) { //* // resolve multisample-framebuffer to renderbuffer and read pixels from it //* GLuint fbo, rb; //* QOpenGLFunctions *gl = m_gl->functions(); //* gl->glGenFramebuffers(1, &fbo); //* gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); //* gl->glGenRenderbuffers(1, &rb); //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); //* gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(), rect.height()); //* gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); //* //* const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); //* if (status != GL_FRAMEBUFFER_COMPLETE) { //* gl->glDeleteRenderbuffers(1, &rb); //* gl->glDeleteFramebuffers(1, &fbo); //* qCWarning(Backend) << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status; //* return img; //* } //* //* m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(), //* 0, 0, rect.width(), rect.height(), //* GL_COLOR_BUFFER_BIT, GL_NEAREST); //* gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); //* gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data()); //* //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); //* //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb); //* gl->glDeleteRenderbuffers(1, &rb); //* gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO); //* gl->glDeleteFramebuffers(1, &fbo); //* } else { //* // read pixels directly from framebuffer //* m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), format, type, data.data()); //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(), m_renderTargetFormat); //* } //* //* return img; } void SubmissionContext::setViewport(const QRectF &viewport, const QSize &surfaceSize) { // // save for later use; this has nothing to do with the viewport but it is // // here that we get to know the surfaceSize from the RenderView. m_surfaceSize = surfaceSize; m_viewport = viewport; QSize size = renderTargetSize(surfaceSize); // Check that the returned size is before calling glViewport if (size.isEmpty()) return; // Qt3D 0------------------> 1 OpenGL 1^ // | | // | | // | | // V | // 1 0---------------------> 1 // The Viewport is defined between 0 and 1 which allows us to automatically // scale to the size of the provided window surface RHI_UNIMPLEMENTED; //* m_gl->functions()->glViewport(m_viewport.x() * size.width(), //* (1.0 - m_viewport.y() - m_viewport.height()) * size.height(), //* m_viewport.width() * size.width(), //* m_viewport.height() * size.height()); } void SubmissionContext::releaseOpenGL() { m_renderBufferHash.clear(); // Stop and destroy the OpenGL logger if (m_debugLogger) { m_debugLogger->stopLogging(); m_debugLogger.reset(nullptr); } } // The OpenGLContext is not current on any surface at this point void SubmissionContext::setOpenGLContext(QOpenGLContext* ctx) { RHI_UNIMPLEMENTED; //* Q_ASSERT(ctx); //* //* releaseOpenGL(); //* m_gl = ctx; } // Called only from RenderThread bool SubmissionContext::activateShader(RHIShader *shader) { RHI_UNIMPLEMENTED; //* if (shader->shaderProgram() != m_activeShader) { //* // Ensure material uniforms are re-applied //* m_material = nullptr; //* //* m_activeShader = shader->shaderProgram(); //* if (Q_LIKELY(m_activeShader != nullptr)) { //* m_activeShader->bind(); //* } else { //* m_glHelper->useProgram(0); //* qWarning() << "No shader program found"; //* return false; //* } //* } return true; } void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments) { RHI_UNIMPLEMENTED; // Set FBO attachments. These are normally textures, except that on Open GL // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is // desired since this cannot be achieved neither with a single texture (not // before GLES 3.2) nor with separate textures (no suitable format for // stencil before 3.1 with the appropriate extension). //* QSize fboSize; //* RHITextureManager *rhiTextureManager = m_renderer->rhiResourceManagers()->rhiTextureManager(); //* const auto attachments_ = attachments.attachments(); //* for (const Attachment &attachment : attachments_) { //* RHITexture *rTex = rhiTextureManager->lookupResource(attachment.m_textureUuid); //* if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) { //* QOpenGLTexture *glTex = rTex ? rTex->getGLTexture() : nullptr; //* if (glTex != nullptr) { //* // The texture can not be rendered simultaniously by another renderer //* Q_ASSERT(!rTex->isExternalRenderingEnabled()); //* if (fboSize.isEmpty()) //* fboSize = QSize(glTex->width(), glTex->height()); //* else //* fboSize = QSize(qMin(fboSize.width(), glTex->width()), qMin(fboSize.height(), glTex->height())); //* m_glHelper->bindFrameBufferAttachment(glTex, attachment); //* } //* } else { //* RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr; //* if (renderBuffer) { //* if (fboSize.isEmpty()) //* fboSize = QSize(renderBuffer->width(), renderBuffer->height()); //* else //* fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()), qMin(fboSize.height(), renderBuffer->height())); //* m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment); //* } //* } //* } //* m_renderTargetsSize.insert(fboId, fboSize); } void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments) { RHI_UNIMPLEMENTED; //* const QVector activeDrawBuffers = attachments.getGlDrawBuffers(); //* //* if (m_glHelper->checkFrameBufferComplete()) { //* if (activeDrawBuffers.size() > 1) {// We need MRT //* if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) { //* // Set up MRT, glDrawBuffers... //* m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data()); //* } //* } //* } else { //* qCWarning(Backend) << "FBO incomplete"; //* } } void SubmissionContext::setActiveMaterial(Material *rmat) { if (m_material == rmat) return; m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial); m_imageContext.deactivateImages(); m_material = rmat; } void SubmissionContext::setCurrentStateSet(RenderStateSet *ss) { if (ss == m_stateSet) return; if (ss) applyStateSet(ss); m_stateSet = ss; } RenderStateSet *SubmissionContext::currentStateSet() const { return m_stateSet; } void SubmissionContext::applyState(const StateVariant &stateVariant) { RHI_UNIMPLEMENTED; //* 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 DepthRangeMask: { //* applyStateHelper(static_cast(stateVariant.constState()), this); //* break; //* } //* //* case RasterModeMask: { //* 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 SubmissionContext::resetMasked(qint64 maskOfStatesToReset) { RHI_UNIMPLEMENTED; //* // TO DO -> Call gcHelper methods instead of raw GL //* // QOpenGLFunctions shouldn't be used here directly //* QOpenGLFunctions *funcs = m_gl->functions(); //* //* if (maskOfStatesToReset & ScissorStateMask) //* funcs->glDisable(GL_SCISSOR_TEST); //* //* if (maskOfStatesToReset & BlendStateMask) //* funcs->glDisable(GL_BLEND); //* //* if (maskOfStatesToReset & StencilWriteStateMask) //* funcs->glStencilMask(0); //* //* if (maskOfStatesToReset & StencilTestStateMask) //* funcs->glDisable(GL_STENCIL_TEST); //* //* if (maskOfStatesToReset & DepthRangeMask) //* depthRange(0.0f, 1.0f); //* //* if (maskOfStatesToReset & DepthTestStateMask) //* funcs->glDisable(GL_DEPTH_TEST); //* //* if (maskOfStatesToReset & DepthWriteStateMask) //* funcs->glDepthMask(GL_TRUE); // reset to default //* //* if (maskOfStatesToReset & FrontFaceStateMask) //* funcs->glFrontFace(GL_CCW); // reset to default //* //* if (maskOfStatesToReset & CullFaceStateMask) //* funcs->glDisable(GL_CULL_FACE); //* //* if (maskOfStatesToReset & DitheringStateMask) //* funcs->glDisable(GL_DITHER); //* //* if (maskOfStatesToReset & AlphaCoverageStateMask) //* setAlphaCoverageEnabled(false); //* //* if (maskOfStatesToReset & PointSizeMask) //* pointSize(false, 1.0f); // reset to default //* //* if (maskOfStatesToReset & PolygonOffsetStateMask) //* funcs->glDisable(GL_POLYGON_OFFSET_FILL); //* //* if (maskOfStatesToReset & ColorStateMask) //* funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); //* //* if (maskOfStatesToReset & ClipPlaneMask) { //* GLint max = maxClipPlaneCount(); //* for (GLint i = 0; i < max; ++i) //* disableClipPlane(i); //* } //* //* if (maskOfStatesToReset & SeamlessCubemapMask) //* setSeamlessCubemap(false); //* //* if (maskOfStatesToReset & StencilOpMask) //* funcs->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); //* //* if (maskOfStatesToReset & LineWidthMask) //* funcs->glLineWidth(1.0f); //* //* #ifndef QT_OPENGL_ES_2 //* if (maskOfStatesToReset & RasterModeMask) //* m_glHelper->rasterMode(GL_FRONT_AND_BACK, GL_FILL); //* #endif } void SubmissionContext::applyStateSet(RenderStateSet *ss) { RenderStateSet* previousStates = currentStateSet(); const StateMaskSet invOurState = ~ss->stateMask(); // generate a mask for each set bit in previous, where we do not have // the corresponding bit set. StateMaskSet stateToReset = 0; if (previousStates) { stateToReset = previousStates->stateMask() & invOurState; qCDebug(RenderStates) << "previous states " << QString::number(previousStates->stateMask(), 2); } qCDebug(RenderStates) << " current states " << QString::number(ss->stateMask(), 2) << "inverse " << QString::number(invOurState, 2) << " -> states to change: " << QString::number(stateToReset, 2); // Reset states that aren't active in the current state set resetMasked(stateToReset); // Apply states that weren't in the previous state or that have // different values const QVector statesToSet = ss->states(); for (const StateVariant &ds : statesToSet) { if (previousStates && previousStates->contains(ds)) continue; applyState(ds); } } void SubmissionContext::clearColor(const QColor &color) { RHI_UNIMPLEMENTED; //* if (m_currClearColorValue != color) { //* m_currClearColorValue = color; //* m_gl->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); //* } } void SubmissionContext::clearDepthValue(float depth) { RHI_UNIMPLEMENTED; //* if (m_currClearDepthValue != depth) { //* m_currClearDepthValue = depth; //* m_gl->functions()->glClearDepthf(depth); //* } } void SubmissionContext::clearStencilValue(int stencil) { RHI_UNIMPLEMENTED; //* if (m_currClearStencilValue != stencil) { //* m_currClearStencilValue = stencil; //* m_gl->functions()->glClearStencil(stencil); //* } } RHIFence SubmissionContext::fenceSync() { RHI_UNIMPLEMENTED; return {}; //* return m_glHelper->fenceSync(); } void SubmissionContext::clientWaitSync(RHIFence sync, GLuint64 nanoSecTimeout) { RHI_UNIMPLEMENTED; //* qDebug() << Q_FUNC_INFO << sync; //* m_glHelper->clientWaitSync(sync, nanoSecTimeout); } void SubmissionContext::waitSync(RHIFence sync) { RHI_UNIMPLEMENTED; //* qDebug() << Q_FUNC_INFO << sync; //* m_glHelper->waitSync(sync); } bool SubmissionContext::wasSyncSignaled(RHIFence sync) { RHI_UNIMPLEMENTED; return true; //* return m_glHelper->wasSyncSignaled(sync); } void SubmissionContext::deleteSync(RHIFence sync) { RHI_UNIMPLEMENTED; //* m_glHelper->deleteSync(sync); } // It will be easier if the QGraphicContext applies the QUniformPack // than the other way around bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) { static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); // Activate textures and update TextureUniform in the pack // with the correct textureUnit // Set the pinned texture of the previous material texture // to pinable so that we should easily find an available texture unit m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial); // Update the uniforms with the correct texture unit id's PackUniformHash &uniformValues = parameterPack.uniforms(); // Fill Texture Uniform Value with proper texture units // so that they can be applied as regular uniforms in a second step for (int i = 0; i < parameterPack.textures().size(); ++i) { RHI_UNIMPLEMENTED; //* const ShaderParameterPack::NamedResource &namedTex = parameterPack.textures().at(i); //* // Given a Texture QNodeId, we retrieve the associated shared RHITexture //* if (uniformValues.contains(namedTex.glslNameId)) { //* RHITexture *t = m_renderer->rhiResourceManagers()->rhiTextureManager()->lookupResource(namedTex.nodeId); //* if (t != nullptr) { //* UniformValue &texUniform = uniformValues.value(namedTex.glslNameId); //* if (texUniform.valueType() == UniformValue::TextureValue) { //* const int texUnit = m_textureContext.activateTexture(TextureSubmissionContext::TextureScopeMaterial, m_gl, t); //* texUniform.data()[namedTex.uniformArrayIndex] = texUnit; //* if (texUnit == -1) { //* if (namedTex.glslNameId != irradianceId && //* namedTex.glslNameId != specularId) { //* // Only return false if we are not dealing with env light textures //* qCWarning(Backend) << "Unable to find suitable Texture Unit"; //* return false; //* } //* } //* } //* } //* } } // Fill Image Uniform Value with proper image units // so that they can be applied as regular uniforms in a second step for (int i = 0; i < parameterPack.images().size(); ++i) { RHI_UNIMPLEMENTED; const ShaderParameterPack::NamedResource &namedTex = parameterPack.images().at(i); // Given a Texture QNodeId, we retrieve the associated shared RHITexture if (uniformValues.contains(namedTex.glslNameId)) { ShaderImage *img = m_renderer->nodeManagers()->shaderImageManager()->lookupResource(namedTex.nodeId); if (img != nullptr) { RHITexture *t = m_renderer->rhiResourceManagers()->rhiTextureManager()->lookupResource(img->textureId()); if (t == nullptr) { qCWarning(Backend) << "Shader Image referencing invalid texture"; continue; } else { UniformValue &imgUniform = uniformValues.value(namedTex.glslNameId); if (imgUniform.valueType() == UniformValue::ShaderImageValue) { const int imgUnit = m_imageContext.activateImage(img, t); imgUniform.data()[namedTex.uniformArrayIndex] = imgUnit; if (imgUnit == -1) { qCWarning(Backend) << "Unable to bind Image to Texture"; return false; } } } } } } QOpenGLShaderProgram *shader = activeShader(); // TO DO: We could cache the binding points somehow and only do the binding when necessary // for SSBO and UBO // Bind Shader Storage block to SSBO and update SSBO const QVector blockToSSBOs = parameterPack.shaderStorageBuffers(); for (const BlockToSSBO b : blockToSSBOs) { RHI_UNIMPLEMENTED; Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); RHIBuffer *ssbo = glBufferForRenderBuffer(cpuBuffer); // bindShaderStorageBlock // This is currently not required as we are introspecting the bindingIndex // value from the shaders and not replacing them, making such a call useless // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex); bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex); // Needed to avoid conflict where the buffer would already // be bound as a VertexArray bindGLBuffer(ssbo, RHIBuffer::ShaderStorageBuffer); ssbo->bindBufferBase(this, b.m_bindingIndex, RHIBuffer::ShaderStorageBuffer); // TO DO: Make sure that there's enough binding points } // Bind UniformBlocks to UBO and update UBO from Buffer // TO DO: Convert ShaderData to Buffer so that we can use that generic process const QVector blockToUBOs = parameterPack.uniformBuffers(); int uboIndex = 0; for (const BlockToUBO &b : blockToUBOs) { RHI_UNIMPLEMENTED; Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); RHIBuffer *ubo = glBufferForRenderBuffer(cpuBuffer); bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex); // Needed to avoid conflict where the buffer would already // be bound as a VertexArray bindGLBuffer(ubo, RHIBuffer::UniformBuffer); ubo->bindBufferBase(this, uboIndex++, RHIBuffer::UniformBuffer); // TO DO: Make sure that there's enough binding points } // Update uniforms in the Default Uniform Block const PackUniformHash values = parameterPack.uniforms(); const QVector activeUniforms = parameterPack.submissionUniforms(); for (const ShaderUniform &uniform : activeUniforms) { // We can use [] as we are sure the the uniform wouldn't // be un activeUniforms if there wasn't a matching value const UniformValue &v = values.value(uniform.m_nameId); // skip invalid textures/images if ((v.valueType() == UniformValue::TextureValue || v.valueType() == UniformValue::ShaderImageValue) && *v.constData() == -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) { RHI_UNIMPLEMENTED; //* // Bind buffer within the current VAO //* RHIBuffer *buf = m_renderer->rhiResourceManagers()->rhiBufferManager()->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(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 HRHIBuffer glBufferHandle = m_renderer->rhiResourceManagers()->rhiBufferManager()->lookupHandle(buffer->peerId()); Q_ASSERT(!glBufferHandle.isNull()); const RHIBuffer::Type attributeType = attributeTypeToGLBufferType(attribute->attributeType()); int typeSize = 0; int attrCount = 0; if (attribute->vertexSize() >= 1 && attribute->vertexSize() <= 4) { attrCount = 1; } else if (attribute->vertexSize() == 9) { typeSize = byteSizeFromType(attributeDataType); attrCount = 3; } else if (attribute->vertexSize() == 16) { typeSize = byteSizeFromType(attributeDataType); attrCount = 4; } else { Q_UNREACHABLE(); } Q_ASSERT(!glBufferHandle.isNull()); VAOVertexAttribute attr; attr.bufferHandle = glBufferHandle; attr.attributeType = attributeType; attr.dataType = attributeDataType; attr.divisor = attribute->divisor(); attr.vertexSize = attribute->vertexSize() / attrCount; attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize); attr.shaderDataType = attributeDescription->m_type; for (int i = 0; i < attrCount; i++) { attr.location = location + i; attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize); enableAttribute(attr); // Save this in the current emulated VAO if (m_currentVAO) m_currentVAO->saveVertexAttribute(attr); } } void SubmissionContext::specifyIndices(Buffer *buffer) { RHIBuffer *buf = glBufferForRenderBuffer(buffer); if (!bindGLBuffer(buf, RHIBuffer::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->rhiResourceManagers()->rhiBufferManager()->lookupHandle(buffer->peerId())); } void SubmissionContext::updateBuffer(Buffer *buffer) { const QHash::iterator it = m_renderBufferHash.find(buffer->peerId()); if (it != m_renderBufferHash.end()) uploadDataToGLBuffer(buffer, m_renderer->rhiResourceManagers()->rhiBufferManager()->data(it.value())); } QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer) { const QHash::iterator it = m_renderBufferHash.find(buffer->peerId()); if (it != m_renderBufferHash.end()) return downloadDataFromGLBuffer(buffer, m_renderer->rhiResourceManagers()->rhiBufferManager()->data(it.value())); return QByteArray(); } void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId) { auto it = m_renderBufferHash.find(bufferId); if (it != m_renderBufferHash.end()) { HRHIBuffer glBuffHandle = it.value(); RHIBuffer *glBuff = m_renderer->rhiResourceManagers()->rhiBufferManager()->data(glBuffHandle); Q_ASSERT(glBuff); // Destroy the GPU resource glBuff->destroy(this); // Destroy the RHIBuffer instance m_renderer->rhiResourceManagers()->rhiBufferManager()->releaseResource(bufferId); // Remove Id - HGLBuffer entry m_renderBufferHash.erase(it); } } bool SubmissionContext::hasGLBufferForBuffer(Buffer *buffer) { const QHash::iterator it = m_renderBufferHash.find(buffer->peerId()); return (it != m_renderBufferHash.end()); } RHIBuffer *SubmissionContext::glBufferForRenderBuffer(Buffer *buf) { if (!m_renderBufferHash.contains(buf->peerId())) m_renderBufferHash.insert(buf->peerId(), createGLBufferFor(buf)); return m_renderer->rhiResourceManagers()->rhiBufferManager()->data(m_renderBufferHash.value(buf->peerId())); } HRHIBuffer SubmissionContext::createGLBufferFor(Buffer *buffer) { m_renderer->rhiResourceManagers()->rhiBufferManager()->getOrCreateResource(buffer->peerId()); return m_renderer->rhiResourceManagers()->rhiBufferManager()->lookupHandle(buffer->peerId()); } bool SubmissionContext::bindGLBuffer(RHIBuffer *buffer, RHIBuffer::Type type) { return buffer->bind(this, type); } void SubmissionContext::uploadDataToGLBuffer(Buffer *buffer, RHIBuffer *b, bool releaseBuffer) { // if (!bindGLBuffer(b, RHIBuffer::ArrayBuffer)) // We're uploading, the type doesn't matter here // qCWarning(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 updates = std::move(buffer->pendingBufferUpdates()); for (auto it = updates.begin(); it != updates.end(); ++it) { auto update = it; // We have a partial update if (update->offset >= 0) { //accumulate sequential updates as single one int bufferSize = update->data.size(); auto it2 = it + 1; while ((it2 != updates.end()) && (it2->offset - update->offset == bufferSize)) { bufferSize += it2->data.size(); ++it2; } update->data.resize(bufferSize); while (it + 1 != it2) { ++it; update->data.replace(it->offset - update->offset, it->data.size(), it->data); it->data.clear(); } // TO DO: based on the number of updates .., it might make sense to // sometime use glMapBuffer rather than glBufferSubData b->update(this, update->data, update->offset); } else { // We have an update that was done by calling QBuffer::setData // which is used to resize or entirely clear the buffer // Note: we use the buffer data directly in that case b->orphan(this); // orphan the buffer b->allocate(this, buffer->data(), false); } } if (releaseBuffer) { b->release(this); } qCDebug(Io) << "uploaded buffer size=" << buffer->data().size(); } QByteArray SubmissionContext::downloadDataFromGLBuffer(Buffer *buffer, RHIBuffer *b) { if (!bindGLBuffer(b, RHIBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here qCWarning(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) { RHI_UNIMPLEMENTED; //* GLuint inputFboId = defaultFboId; //* bool inputBufferIsDefault = true; //* if (!inputRenderTargetId.isNull()) { //* RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(inputRenderTargetId); //* if (renderTarget) { //* AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); //* if (m_renderTargets.contains(inputRenderTargetId)) //* inputFboId = updateRenderTarget(inputRenderTargetId, attachments, false); //* else //* inputFboId = createRenderTarget(inputRenderTargetId, attachments); //* } //* inputBufferIsDefault = false; //* } //* //* GLuint outputFboId = defaultFboId; //* bool outputBufferIsDefault = true; //* if (!outputRenderTargetId.isNull()) { //* RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(outputRenderTargetId); //* if (renderTarget) { //* AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager()); //* if (m_renderTargets.contains(outputRenderTargetId)) //* outputFboId = updateRenderTarget(outputRenderTargetId, attachments, false); //* else //* outputFboId = createRenderTarget(outputRenderTargetId, attachments); //* } //* outputBufferIsDefault = false; //* } //* //* // Up until this point the input and output rects are normal Qt rectangles. //* // Convert them to GL rectangles (Y at bottom). //* const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[inputFboId].height(); //* const GLint srcX0 = inputRect.left(); //* const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height()); //* const GLint srcX1 = srcX0 + inputRect.width(); //* const GLint srcY1 = srcY0 + inputRect.height(); //* //* const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[outputFboId].height(); //* const GLint dstX0 = outputRect.left(); //* const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height()); //* const GLint dstX1 = dstX0 + outputRect.width(); //* const GLint dstY1 = dstY0 + outputRect.height(); //* //* //Get the last bounded framebuffers //* const GLuint lastDrawFboId = boundFrameBufferObject(); //* //* // Activate input framebuffer for reading //* bindFramebuffer(inputFboId, GraphicsHelperInterface::FBORead); //* //* // Activate output framebuffer for writing //* bindFramebuffer(outputFboId, GraphicsHelperInterface::FBODraw); //* //* //Bind texture //* if (!inputBufferIsDefault) //* readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint); //* //* if (!outputBufferIsDefault) { //* // Note that we use glDrawBuffers, not glDrawBuffer. The //* // latter is not available with GLES. //* const int buf = outputAttachmentPoint; //* drawBuffers(1, &buf); //* } //* //* // Blit framebuffer //* const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR; //* m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, //* dstX0, dstY0, dstX1, dstY1, //* GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, //* mode); //* //* // Reset draw buffer //* bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw); //* if (outputAttachmentPoint != QRenderTargetOutput::Color0) { //* const int buf = QRenderTargetOutput::Color0; //* drawBuffers(1, &buf); //* } } } // namespace Rhi } // namespace Render } // namespace Qt3DRender of namespace QT_END_NAMESPACE