int QOpenGLTextureGlyphCache::maxTextureHeight() const
{
    QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
    if (ctx == 0)
        return QImageTextureGlyphCache::maxTextureHeight();

    if (ctx->d_func()->workaround_brokenTexSubImage)
        return qMin(1024, ctx->d_func()->maxTextureSize());
    else
        return ctx->d_func()->maxTextureSize();
}
/*!
    Blits from the \a sourceRect rectangle in the \a source framebuffer
    object to the \a targetRect rectangle in the \a target framebuffer object.

    If \a source or \a target is 0, the default framebuffer will be used
    instead of a framebuffer object as source or target respectively.

    The \a buffers parameter should be a mask consisting of any combination of
    \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
    \c GL_STENCIL_BUFFER_BIT.  Any buffer type that is not present both
    in the source and target buffers is ignored.

    The \a sourceRect and \a targetRect rectangles may have different sizes;
    in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
    \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
    \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
    interpolation should be used when scaling is performed.

    If \a source equals \a target a copy is performed within the same buffer.
    Results are undefined if the source and target rectangles overlap and
    have different sizes. The sizes must also be the same if any of the
    framebuffer objects are multisample framebuffers.

    Note that the scissor test will restrict the blit area if enabled.

    This function will have no effect unless hasOpenGLFramebufferBlit() returns
    true.

    \sa hasOpenGLFramebufferBlit()
*/
void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
                                           QOpenGLFramebufferObject *source, const QRect &sourceRect,
                                           GLbitfield buffers,
                                           GLenum filter)
{
    QOpenGLContext *ctx = QOpenGLContext::currentContext();
    if (!ctx)
        return;

    QOpenGLExtensions extensions(ctx);
    if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
        return;

    const int sx0 = sourceRect.left();
    const int sx1 = sourceRect.left() + sourceRect.width();
    const int sy0 = sourceRect.top();
    const int sy1 = sourceRect.top() + sourceRect.height();

    const int tx0 = targetRect.left();
    const int tx1 = targetRect.left() + targetRect.width();
    const int ty0 = targetRect.top();
    const int ty1 = targetRect.top() + targetRect.height();

    extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : 0);
    extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : 0);

    extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1,
                                 tx0, ty0, tx1, ty1,
                                 buffers, filter);

    extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
}
/*!
    \fn bool QOpenGLFramebufferObject::bindDefault()

    Switches rendering back to the default, windowing system provided
    framebuffer.
    Returns \c true upon success, false otherwise.

    \sa bind(), release()
*/
bool QOpenGLFramebufferObject::bindDefault()
{
    QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
    QOpenGLFunctions functions(ctx);

    if (ctx) {
        ctx->d_func()->current_fbo = ctx->defaultFramebufferObject();
        functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
#ifdef QT_DEBUG
    } else {
        qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
#endif
    }

    return ctx != 0;
}
/*!
    Sets the attachments of the framebuffer object to \a attachment.

    This can be used to free or reattach the depth and stencil buffer
    attachments as needed.
 */
void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
{
    Q_D(QOpenGLFramebufferObject);
    if (attachment == d->fbo_attachment || !isValid())
        return;
    QOpenGLContext *current = QOpenGLContext::currentContext();
    if (!current)
        return;
#ifdef QT_DEBUG
    if (current->shareGroup() != d->fbo_guard->group())
        qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context");
#endif
    d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
    d->initAttachments(current, attachment);
    if (current->d_func()->current_fbo != d->fbo())
        d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
}
void QOpenGLTextureGlyphCache::createTextureData(int width, int height)
{
    QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
    if (ctx == 0) {
        qWarning("QOpenGLTextureGlyphCache::createTextureData: Called with no context");
        return;
    }

    // create in QImageTextureGlyphCache baseclass is meant to be called
    // only to create the initial image and does not preserve the content,
    // so we don't call when this function is called from resize.
    if (ctx->d_func()->workaround_brokenFBOReadBack && image().isNull())
        QImageTextureGlyphCache::createTextureData(width, height);

    // Make the lower glyph texture size 16 x 16.
    if (width < 16)
        width = 16;
    if (height < 16)
        height = 16;

    if (m_textureResource && !m_textureResource->m_texture) {
        delete m_textureResource;
        m_textureResource = 0;
    }

    if (!m_textureResource)
        m_textureResource = new QOpenGLGlyphTexture(ctx);

    glGenTextures(1, &m_textureResource->m_texture);
    glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);

    m_textureResource->m_width = width;
    m_textureResource->m_height = height;

    if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {
        QVarLengthArray<uchar> data(width * height * 4);
        for (int i = 0; i < data.size(); ++i)
            data[i] = 0;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
    } else {
        QVarLengthArray<uchar> data(width * height);
        for (int i = 0; i < data.size(); ++i)
            data[i] = 0;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]);
    }

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    m_filterMode = Nearest;
}
/*!
    \fn bool QOpenGLFramebufferObject::release()

    Switches rendering back to the default, windowing system provided
    framebuffer.
    Returns \c true upon success, false otherwise.

    \sa bind()
*/
bool QOpenGLFramebufferObject::release()
{
    if (!isValid())
        return false;

    QOpenGLContext *current = QOpenGLContext::currentContext();
    if (!current)
        return false;

    Q_D(QOpenGLFramebufferObject);
#ifdef QT_DEBUG
    if (current->shareGroup() != d->fbo_guard->group())
        qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
#endif

    if (current) {
        current->d_func()->current_fbo = current->defaultFramebufferObject();
        d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
    }

    return true;
}
/*!
   \fn GLuint QOpenGLFramebufferObject::takeTexture()

   Returns the texture id for the texture attached to this framebuffer
   object. The ownership of the texture is transferred to the caller.

   If the framebuffer object is currently bound, an implicit release()
   will be done. During the next call to bind() a new texture will be
   created.

   If a multisample framebuffer object is used, then there is no
   texture and the return value from this function will be invalid.
   Similarly, incomplete framebuffer objects will also return 0.

   \since 5.3

   \sa texture(), bind(), release()
 */
GLuint QOpenGLFramebufferObject::takeTexture()
{
    Q_D(QOpenGLFramebufferObject);
    GLuint id = 0;
    if (isValid() && d->texture_guard) {
        QOpenGLContext *current = QOpenGLContext::currentContext();
        if (current && current->shareGroup() == d->fbo_guard->group() && current->d_func()->current_fbo == d->fbo())
            release();
        id = d->texture_guard->id();
        // Do not call free() on texture_guard, just null it out.
        // This way the texture will not be deleted when the guard is destroyed.
        d->texture_guard = 0;
    }
    return id;
}
/*!
    \fn bool QOpenGLFramebufferObject::bind()

    Switches rendering from the default, windowing system provided
    framebuffer to this framebuffer object.
    Returns \c true upon success, false otherwise.

    \note If takeTexture() was called, a new texture is created and associated
    with the framebuffer object. This is potentially expensive and changes the
    context state (the currently bound texture).

    \sa release()
*/
bool QOpenGLFramebufferObject::bind()
{
    if (!isValid())
        return false;
    Q_D(QOpenGLFramebufferObject);
    QOpenGLContext *current = QOpenGLContext::currentContext();
    if (!current)
        return false;
#ifdef QT_DEBUG
    if (current->shareGroup() != d->fbo_guard->group())
        qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
#endif
    d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
    if (d->texture_guard || d->format.samples() != 0)
        d->valid = d->checkFramebufferStatus(current);
    else
        d->initTexture(d->format.textureTarget(), d->format.internalTextureFormat(), d->size, d->format.mipmap());
    if (d->valid && current)
        current->d_func()->current_fbo = d->fbo();
    return d->valid;
}
Example #9
0
/*!
    Copies the pbuffer contents into the texture specified with \a
    texture_id.

    The texture must be of the same size and format as the pbuffer.

    Example:

    \snippet code/src_opengl_qglpixelbuffer.cpp 1

    An alternative on Windows and Mac OS X systems that support the
    \c render_texture extension is to use bindToDynamicTexture() to
    get dynamic updates of the texture.

    \sa generateDynamicTexture(), bindToDynamicTexture()
*/
void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const
{
    Q_D(const QGLPixelBuffer);
    if (d->invalid || !d->fbo)
        return;

    QOpenGLContext *ctx = QOpenGLContext::currentContext();
    if (!ctx)
        return;

#undef glBindFramebuffer

#ifndef GL_READ_FRAMEBUFFER
#define GL_READ_FRAMEBUFFER 0x8CA8
#endif

#ifndef GL_DRAW_FRAMEBUFFER
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#endif

    QOpenGLExtensions extensions(ctx);

    if (d->blit_fbo) {
        QOpenGLFramebufferObject::blitFramebuffer(d->blit_fbo, d->fbo);
        extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, d->blit_fbo->handle());
    }

    glBindTexture(GL_TEXTURE_2D, texture_id);
#ifndef QT_OPENGL_ES
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, d->req_size.width(), d->req_size.height(), 0);
#else
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0);
#endif

    if (d->blit_fbo)
        extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->d_func()->current_fbo);
}
void QOpenGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
{
    QOpenGLContext *ctx = QOpenGLContext::currentContext();
    if (ctx == 0) {
        qWarning("QOpenGLTextureGlyphCache::fillTexture: Called with no context");
        return;
    }

    if (ctx->d_func()->workaround_brokenFBOReadBack) {
        QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);

        glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
        const QImage &texture = image();
        const uchar *bits = texture.constBits();
        bits += c.y * texture.bytesPerLine() + c.x;
        for (int i=0; i<c.h; ++i) {
            glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits);
            bits += texture.bytesPerLine();
        }
        return;
    }

    QImage mask = textureMapForGlyph(glyph, subPixelPosition);
    const int maskWidth = mask.width();
    const int maskHeight = mask.height();

    if (mask.format() == QImage::Format_Mono) {
        mask = mask.convertToFormat(QImage::Format_Indexed8);
        for (int y = 0; y < maskHeight; ++y) {
            uchar *src = (uchar *) mask.scanLine(y);
            for (int x = 0; x < maskWidth; ++x)
                src[x] = -src[x]; // convert 0 and 1 into 0 and 255
        }
    } else if (mask.format() == QImage::Format_RGB32) {
        // Make the alpha component equal to the average of the RGB values.
        // This is needed when drawing sub-pixel antialiased text on translucent targets.
        for (int y = 0; y < maskHeight; ++y) {
            quint32 *src = (quint32 *) mask.scanLine(y);
            for (int x = 0; x < maskWidth; ++x) {
                uchar r = src[x] >> 16;
                uchar g = src[x] >> 8;
                uchar b = src[x];
                quint32 avg = (quint32(r) + quint32(g) + quint32(b) + 1) / 3; // "+1" for rounding.
                src[x] = (src[x] & 0x00ffffff) | (avg << 24);
            }
        }
    }

    glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
    if (mask.format() == QImage::Format_RGB32) {
#if defined(QT_OPENGL_ES_2)
        // ###TODO Ensure extension is actually present on ES2
        glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, mask.bits());
#else
        glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits());
#endif
    } else {
        // glTexSubImage2D() might cause some garbage to appear in the texture if the mask width is
        // not a multiple of four bytes. The bug appeared on a computer with 32-bit Windows Vista
        // and nVidia GeForce 8500GT. GL_UNPACK_ALIGNMENT is set to four bytes, 'mask' has a
        // multiple of four bytes per line, and most of the glyph shows up correctly in the
        // texture, which makes me think that this is a driver bug.
        // One workaround is to make sure the mask width is a multiple of four bytes, for instance
        // by converting it to a format with four bytes per pixel. Another is to copy one line at a
        // time.

#if 0
        if (!ctx->d_func()->workaround_brokenAlphaTexSubImage_init) {
            // don't know which driver versions exhibit this bug, so be conservative for now
            const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
            glctx->d_func()->workaround_brokenAlphaTexSubImage = versionString.indexOf("NVIDIA") >= 0;
            glctx->d_func()->workaround_brokenAlphaTexSubImage_init = true;
        }
#endif

#if 0
        if (ctx->d_func()->workaround_brokenAlphaTexSubImage) {
            for (int i = 0; i < maskHeight; ++i)
                glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i));
        } else {
#endif
            glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits());
//        }
    }
}

int QOpenGLTextureGlyphCache::glyphPadding() const
{
    return 1;
}

int QOpenGLTextureGlyphCache::maxTextureWidth() const
{
    QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
    if (ctx == 0)
        return QImageTextureGlyphCache::maxTextureWidth();
    else
        return ctx->d_func()->maxTextureSize();
}
void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height)
{
    QOpenGLContext *ctx = QOpenGLContext::currentContext();
    if (ctx == 0) {
        qWarning("QOpenGLTextureGlyphCache::resizeTextureData: Called with no context");
        return;
    }

    int oldWidth = m_textureResource->m_width;
    int oldHeight = m_textureResource->m_height;

    // Make the lower glyph texture size 16 x 16.
    if (width < 16)
        width = 16;
    if (height < 16)
        height = 16;

    GLuint oldTexture = m_textureResource->m_texture;
    createTextureData(width, height);

    if (ctx->d_func()->workaround_brokenFBOReadBack) {
        QImageTextureGlyphCache::resizeTextureData(width, height);
        Q_ASSERT(image().depth() == 8);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits());
        glDeleteTextures(1, &oldTexture);
        return;
    }

    // ### the QTextureGlyphCache API needs to be reworked to allow
    // ### resizeTextureData to fail

    QOpenGLFunctions funcs(ctx);

    funcs.glBindFramebuffer(GL_FRAMEBUFFER, m_textureResource->m_fbo);

    GLuint tmp_texture;
    glGenTextures(1, &tmp_texture);
    glBindTexture(GL_TEXTURE_2D, tmp_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    m_filterMode = Nearest;
    glBindTexture(GL_TEXTURE_2D, 0);
    funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                           GL_TEXTURE_2D, tmp_texture, 0);

    funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
    glBindTexture(GL_TEXTURE_2D, oldTexture);

    if (pex != 0)
        pex->transferMode(BrushDrawingMode);

    glDisable(GL_STENCIL_TEST);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_SCISSOR_TEST);
    glDisable(GL_BLEND);

    glViewport(0, 0, oldWidth, oldHeight);

    QOpenGLShaderProgram *blitProgram = 0;
    if (pex == 0) {
        if (m_blitProgram == 0) {
            m_blitProgram = new QOpenGLShaderProgram(ctx);

            {
                QString source;
                source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader));
                source.append(QLatin1String(qopenglslUntransformedPositionVertexShader));

                QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_blitProgram);
                vertexShader->compileSourceCode(source);

                m_blitProgram->addShader(vertexShader);
            }

            {
                QString source;
                source.append(QLatin1String(qopenglslMainFragmentShader));
                source.append(QLatin1String(qopenglslImageSrcFragmentShader));

                QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_blitProgram);
                fragmentShader->compileSourceCode(source);

                m_blitProgram->addShader(fragmentShader);
            }

            m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
            m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);

            m_blitProgram->link();
        }

        funcs.glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_vertexCoordinateArray);
        funcs.glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureCoordinateArray);

        m_blitProgram->bind();
        m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
        m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
        m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR));

        blitProgram = m_blitProgram;

    } else {
        pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray);
        pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray);

        pex->shaderManager->useBlitProgram();
        blitProgram = pex->shaderManager->blitProgram();
    }

    blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);

    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);

    funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                    GL_RENDERBUFFER, 0);
    glDeleteTextures(1, &tmp_texture);
    glDeleteTextures(1, &oldTexture);

    funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);

    if (pex != 0) {
        glViewport(0, 0, pex->width, pex->height);
        pex->updateClipScissorTest();
    } else {
        m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
        m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
    }
}
void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSize &sz,
                                       QOpenGLFramebufferObject::Attachment attachment,
                                       GLenum texture_target, GLenum internal_format,
                                       GLint samples, bool mipmap)
{
    QOpenGLContext *ctx = QOpenGLContext::currentContext();

    funcs.initializeOpenGLFunctions();

    if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers))
        return;


    // Fall back to using a normal non-msaa FBO if we don't have support for MSAA
    if (!funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
            || !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) {
        samples = 0;
    }

#ifndef QT_OPENGL_ES_2
    GLint maxSamples;
    funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
    samples = qBound(0, int(samples), int(maxSamples));
#endif

    size = sz;
    target = texture_target;
    // texture dimensions

    QT_RESET_GLERROR(); // reset error state
    GLuint fbo = 0;

    funcs.glGenFramebuffers(1, &fbo);
    funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);

    GLuint color_buffer = 0;

    QT_CHECK_GLERROR();
    // init texture
    if (samples == 0) {
        initTexture(texture_target, internal_format, size, mipmap);
    } else {
        mipmap = false;
        funcs.glGenRenderbuffers(1, &color_buffer);
        funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
        funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internal_format, size.width(), size.height());
        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                             GL_RENDERBUFFER, color_buffer);
        QT_CHECK_GLERROR();
        valid = checkFramebufferStatus(ctx);

        if (valid) {
            funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
            color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
        }
    }

    format.setTextureTarget(target);
    format.setSamples(int(samples));
    format.setInternalTextureFormat(internal_format);
    format.setMipmap(mipmap);

    initAttachments(ctx, attachment);

    funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
    if (valid) {
        fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
    } else {
        if (color_buffer_guard) {
            color_buffer_guard->free();
            color_buffer_guard = 0;
        } else if (texture_guard) {
            texture_guard->free();
            texture_guard = 0;
        }
        funcs.glDeleteFramebuffers(1, &fbo);
    }
    QT_CHECK_GLERROR();
}
bool QOpenGLFramebufferObject::isBound() const
{
    Q_D(const QOpenGLFramebufferObject);
    QOpenGLContext *current = QOpenGLContext::currentContext();
    return current ? current->d_func()->current_fbo == d->fbo() : false;
}