Exemplo n.º 1
0
void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
                                       QGLFramebufferObject::Attachment attachment,
                                       GLenum texture_target, GLenum internal_format,
                                       GLint samples, bool mipmap)
{
    QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());

    funcs.initializeOpenGLFunctions();

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

    ctx->d_ptr->refreshCurrentFbo();

    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 texture = 0;
    GLuint color_buffer = 0;
    GLuint depth_buffer = 0;
    GLuint stencil_buffer = 0;

    QT_CHECK_GLERROR();
    // init texture
    if (samples == 0) {
        funcs.glGenTextures(1, &texture);
        funcs.glBindTexture(target, texture);
        funcs.glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
                           GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        if (mipmap) {
            int width = size.width();
            int height = size.height();
            int level = 0;
            while (width > 1 || height > 1) {
                width = qMax(1, width >> 1);
                height = qMax(1, height >> 1);
                ++level;
                funcs.glTexImage2D(target, level, internal_format, width, height, 0,
                                   GL_RGBA, GL_UNSIGNED_BYTE, NULL);
            }
        }
        funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                target, texture, 0);

        QT_CHECK_GLERROR();
        valid = checkFramebufferStatus();
        funcs.glBindTexture(target, 0);

        color_buffer = 0;
    } else {
void QOpenGLFramebufferObjectPrivate::initTexture(GLenum target, GLenum internal_format,
                                                  const QSize &size, bool mipmap)
{
    QOpenGLContext *ctx = QOpenGLContext::currentContext();
    GLuint texture = 0;

    funcs.glGenTextures(1, &texture);
    funcs.glBindTexture(target, texture);

    funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    funcs.glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
                       GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    if (mipmap) {
        int width = size.width();
        int height = size.height();
        int level = 0;
        while (width > 1 || height > 1) {
            width = qMax(1, width >> 1);
            height = qMax(1, height >> 1);
            ++level;
            funcs.glTexImage2D(target, level, internal_format, width, height, 0,
                               GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        }
    }
    funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                 target, texture, 0);

    QT_CHECK_GLERROR();
    funcs.glBindTexture(target, 0);
    valid = checkFramebufferStatus(ctx);
    if (valid)
        texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
    else
        funcs.glDeleteTextures(1, &texture);
}
Exemplo n.º 3
0
void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
                                       QGLFramebufferObject::Attachment attachment,
                                       GLenum texture_target, GLenum internal_format, GLint samples)
{
    QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
    fbo_guard.setContext(ctx);

    bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
    if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
        return;

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

    QT_RESET_GLERROR(); // reset error state
    GLuint fbo = 0;
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
    fbo_guard.setId(fbo);

    glDevice.setFBO(q, attachment);

    QT_CHECK_GLERROR();
    // init texture
    if (samples == 0) {
        glGenTextures(1, &texture);
        glBindTexture(target, texture);
        glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#ifndef QT_OPENGL_ES
        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#else
        glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
        glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                target, texture, 0);

        QT_CHECK_GLERROR();
        valid = checkFramebufferStatus();
        glBindTexture(target, 0);

        color_buffer = 0;
    } else {
        GLint maxSamples;
        glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);

        samples = qBound(1, int(samples), int(maxSamples));

        glGenRenderbuffers(1, &color_buffer);
        glBindRenderbuffer(GL_RENDERBUFFER_EXT, color_buffer);
        if (glRenderbufferStorageMultisampleEXT) {
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                internal_format, size.width(), size.height());
        } else {
            samples = 0;
            glRenderbufferStorage(GL_RENDERBUFFER_EXT, internal_format,
                size.width(), size.height());
        }

        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                                     GL_RENDERBUFFER_EXT, color_buffer);

        QT_CHECK_GLERROR();
        valid = checkFramebufferStatus();

        if (valid)
            glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &samples);
    }

    if (attachment == QGLFramebufferObject::CombinedDepthStencil
        && (QGLExtensions::glExtensions() & QGLExtensions::PackedDepthStencil)) {
        // depth and stencil buffer needs another extension
        glGenRenderbuffers(1, &depth_stencil_buffer);
        Q_ASSERT(!glIsRenderbuffer(depth_stencil_buffer));
        glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_stencil_buffer);
        Q_ASSERT(glIsRenderbuffer(depth_stencil_buffer));
        if (samples != 0 && glRenderbufferStorageMultisampleEXT)
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());
        else
            glRenderbufferStorage(GL_RENDERBUFFER_EXT,
                GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());

        GLint i = 0;
        glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT, depth_stencil_buffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT, depth_stencil_buffer);
        fbo_attachment = QGLFramebufferObject::CombinedDepthStencil;

        valid = checkFramebufferStatus();
        if (!valid)
            glDeleteRenderbuffers(1, &depth_stencil_buffer);
    } else if (attachment == QGLFramebufferObject::Depth
               || attachment == QGLFramebufferObject::CombinedDepthStencil)
    {
        glGenRenderbuffers(1, &depth_stencil_buffer);
        Q_ASSERT(!glIsRenderbuffer(depth_stencil_buffer));
        glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_stencil_buffer);
        Q_ASSERT(glIsRenderbuffer(depth_stencil_buffer));
        if (samples != 0 && glRenderbufferStorageMultisampleEXT) {
#ifdef QT_OPENGL_ES
#define GL_DEPTH_COMPONENT16 0x81A5
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                GL_DEPTH_COMPONENT16, size.width(), size.height());
#else
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                GL_DEPTH_COMPONENT, size.width(), size.height());
#endif
        } else {
#ifdef QT_OPENGL_ES
#define GL_DEPTH_COMPONENT16 0x81A5
            glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, size.width(), size.height());
#else
            glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height());
#endif
        }
        GLint i = 0;
        glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT, depth_stencil_buffer);
        fbo_attachment = QGLFramebufferObject::Depth;
        valid = checkFramebufferStatus();
        if (!valid)
            glDeleteRenderbuffers(1, &depth_stencil_buffer);
    } else {
        fbo_attachment = QGLFramebufferObject::NoAttachment;
    }

    glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
    if (!valid) {
        if (color_buffer)
            glDeleteRenderbuffers(1, &color_buffer);
        else
            glDeleteTextures(1, &texture);
        glDeleteFramebuffers(1, &fbo);
        fbo_guard.setId(0);
    }
    QT_CHECK_GLERROR();

    format.setTextureTarget(target);
    format.setSamples(int(samples));
    format.setAttachment(fbo_attachment);
    format.setInternalTextureFormat(internal_format);
}
void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment)
{
    int samples = format.samples();

    // free existing attachments
    if (depth_buffer_guard) {
        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
        depth_buffer_guard->free();
    }
    if (stencil_buffer_guard) {
        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
        if (stencil_buffer_guard != depth_buffer_guard)
            stencil_buffer_guard->free();
    }

    depth_buffer_guard = 0;
    stencil_buffer_guard = 0;

    GLuint depth_buffer = 0;
    GLuint stencil_buffer = 0;

    // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
    // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
    // might not be supported while separate buffers are, according to QTBUG-12861.

    if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
        && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
    {
        // depth and stencil buffer needs another extension
        funcs.glGenRenderbuffers(1, &depth_buffer);
        funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
        Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
        if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
            funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
                GL_DEPTH24_STENCIL8, size.width(), size.height());
        else
            funcs.glRenderbufferStorage(GL_RENDERBUFFER,
                GL_DEPTH24_STENCIL8, size.width(), size.height());

        stencil_buffer = depth_buffer;
        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                     GL_RENDERBUFFER, depth_buffer);
        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                     GL_RENDERBUFFER, stencil_buffer);

        valid = checkFramebufferStatus(ctx);
        if (!valid) {
            funcs.glDeleteRenderbuffers(1, &depth_buffer);
            stencil_buffer = depth_buffer = 0;
        }
    }

    if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
        || (attachment == QOpenGLFramebufferObject::Depth)))
    {
        funcs.glGenRenderbuffers(1, &depth_buffer);
        funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
        Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
        if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
            if (ctx->isOpenGLES()) {
                if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24))
                    funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
                                                           GL_DEPTH_COMPONENT24, size.width(), size.height());
                else
                    funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
                                                           GL_DEPTH_COMPONENT16, size.width(), size.height());
            } else {
                funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
                                                       GL_DEPTH_COMPONENT, size.width(), size.height());
            }
        } else {
            if (ctx->isOpenGLES()) {
                if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
                    funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
                                                size.width(), size.height());
                } else {
                    funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
                                                size.width(), size.height());
                }
            } else {
                funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
            }
        }
        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                     GL_RENDERBUFFER, depth_buffer);
        valid = checkFramebufferStatus(ctx);
        if (!valid) {
            funcs.glDeleteRenderbuffers(1, &depth_buffer);
            depth_buffer = 0;
        }
    }

    if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) {
        funcs.glGenRenderbuffers(1, &stencil_buffer);
        funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer);
        Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer));

#ifdef QT_OPENGL_ES
        GLenum storage = GL_STENCIL_INDEX8;
#else
        GLenum storage = ctx->isOpenGLES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
#endif

        if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
            funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, size.width(), size.height());
        else
            funcs.glRenderbufferStorage(GL_RENDERBUFFER, storage, size.width(), size.height());

        funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                  GL_RENDERBUFFER, stencil_buffer);
        valid = checkFramebufferStatus(ctx);
        if (!valid) {
            funcs.glDeleteRenderbuffers(1, &stencil_buffer);
            stencil_buffer = 0;
        }
    }

    // The FBO might have become valid after removing the depth or stencil buffer.
    valid = checkFramebufferStatus(ctx);

    if (depth_buffer && stencil_buffer) {
        fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
    } else if (depth_buffer) {
        fbo_attachment = QOpenGLFramebufferObject::Depth;
    } else {
        fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
    }

    if (valid) {
        if (depth_buffer)
            depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
        if (stencil_buffer) {
            if (stencil_buffer == depth_buffer)
                stencil_buffer_guard = depth_buffer_guard;
            else
                stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
        }
    } else {
        if (depth_buffer)
            funcs.glDeleteRenderbuffers(1, &depth_buffer);
        if (stencil_buffer && depth_buffer != stencil_buffer)
            funcs.glDeleteRenderbuffers(1, &stencil_buffer);
    }
    QT_CHECK_GLERROR();

    format.setAttachment(fbo_attachment);
}
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();
}
Exemplo n.º 6
0
void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
                                       QGLFramebufferObject::Attachment attachment,
                                       GLenum texture_target, GLenum internal_format, GLint samples)
{
    QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
    fbo_guard.setContext(ctx);

    bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
    if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
        return;

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

    QT_RESET_GLERROR(); // reset error state
    GLuint fbo = 0;
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
    fbo_guard.setId(fbo);

    glDevice.setFBO(q, attachment);

    QT_CHECK_GLERROR();
    // init texture
    if (samples == 0) {
        glGenTextures(1, &texture);
        glBindTexture(target, texture);
        glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#ifndef QT_OPENGL_ES
        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#else
        glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
        glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                target, texture, 0);

        QT_CHECK_GLERROR();
        valid = checkFramebufferStatus();
        glBindTexture(target, 0);

        color_buffer = 0;
    } else {
        GLint maxSamples;
        glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);

        samples = qBound(0, int(samples), int(maxSamples));

        glGenRenderbuffers(1, &color_buffer);
        glBindRenderbuffer(GL_RENDERBUFFER_EXT, color_buffer);
        if (glRenderbufferStorageMultisampleEXT && samples > 0) {
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                internal_format, size.width(), size.height());
        } else {
            samples = 0;
            glRenderbufferStorage(GL_RENDERBUFFER_EXT, internal_format,
                size.width(), size.height());
        }

        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                                     GL_RENDERBUFFER_EXT, color_buffer);

        QT_CHECK_GLERROR();
        valid = checkFramebufferStatus();

        if (valid)
            glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &samples);
    }

    // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
    // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
    // might not be supported while separate buffers are, according to QTBUG-12861.

    if (attachment == QGLFramebufferObject::CombinedDepthStencil
        && (QGLExtensions::glExtensions() & QGLExtensions::PackedDepthStencil)) {
        // depth and stencil buffer needs another extension
        glGenRenderbuffers(1, &depth_buffer);
        Q_ASSERT(!glIsRenderbuffer(depth_buffer));
        glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer);
        Q_ASSERT(glIsRenderbuffer(depth_buffer));
        if (samples != 0 && glRenderbufferStorageMultisampleEXT)
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());
        else
            glRenderbufferStorage(GL_RENDERBUFFER_EXT,
                GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());

        stencil_buffer = depth_buffer;
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT, depth_buffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT, stencil_buffer);

        valid = checkFramebufferStatus();
        if (!valid) {
            glDeleteRenderbuffers(1, &depth_buffer);
            stencil_buffer = depth_buffer = 0;
        }
    }

    if (depth_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil
        || (attachment == QGLFramebufferObject::Depth)))
    {
        glGenRenderbuffers(1, &depth_buffer);
        Q_ASSERT(!glIsRenderbuffer(depth_buffer));
        glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer);
        Q_ASSERT(glIsRenderbuffer(depth_buffer));
        if (samples != 0 && glRenderbufferStorageMultisampleEXT) {
#ifdef QT_OPENGL_ES
            if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) {
                glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                    GL_DEPTH_COMPONENT24_OES, size.width(), size.height());
            } else {
                glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                    GL_DEPTH_COMPONENT16, size.width(), size.height());
            }
#else
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                GL_DEPTH_COMPONENT, size.width(), size.height());
#endif
        } else {
#ifdef QT_OPENGL_ES
            if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) {
                glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_OES, 
                                        size.width(), size.height());
            } else {
                glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, 
                                        size.width(), size.height());
            }
#else
            glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height());
#endif
        }
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT, depth_buffer);
        valid = checkFramebufferStatus();
        if (!valid) {
            glDeleteRenderbuffers(1, &depth_buffer);
            depth_buffer = 0;
        }
    }

    if (stencil_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil)) {
        glGenRenderbuffers(1, &stencil_buffer);
        Q_ASSERT(!glIsRenderbuffer(stencil_buffer));
        glBindRenderbuffer(GL_RENDERBUFFER_EXT, stencil_buffer);
        Q_ASSERT(glIsRenderbuffer(stencil_buffer));
        if (samples != 0 && glRenderbufferStorageMultisampleEXT) {
#ifdef QT_OPENGL_ES
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                GL_STENCIL_INDEX8_EXT, size.width(), size.height());
#else
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
                GL_STENCIL_INDEX, size.width(), size.height());
#endif
        } else {
#ifdef QT_OPENGL_ES
            glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT,
                                  size.width(), size.height());
#else
            glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
                                  size.width(), size.height());
#endif
        }
        glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
                                  GL_RENDERBUFFER_EXT, stencil_buffer);
        valid = checkFramebufferStatus();
        if (!valid) {
            glDeleteRenderbuffers(1, &stencil_buffer);
            stencil_buffer = 0;
        }
    }

    // The FBO might have become valid after removing the depth or stencil buffer.
    valid = checkFramebufferStatus();

    if (depth_buffer && stencil_buffer) {
        fbo_attachment = QGLFramebufferObject::CombinedDepthStencil;
    } else if (depth_buffer) {
        fbo_attachment = QGLFramebufferObject::Depth;
    } else {
        fbo_attachment = QGLFramebufferObject::NoAttachment;
    }

    glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
    if (!valid) {
        if (color_buffer)
            glDeleteRenderbuffers(1, &color_buffer);
        else
            glDeleteTextures(1, &texture);
        if (depth_buffer)
            glDeleteRenderbuffers(1, &depth_buffer);
        if (stencil_buffer && depth_buffer != stencil_buffer)
            glDeleteRenderbuffers(1, &stencil_buffer);
        glDeleteFramebuffers(1, &fbo);
        fbo_guard.setId(0);
    }
    QT_CHECK_GLERROR();

    format.setTextureTarget(target);
    format.setSamples(int(samples));
    format.setAttachment(fbo_attachment);
    format.setInternalTextureFormat(internal_format);
}