Edit

kc3-lang/angle/src/libANGLE/Framebuffer.cpp

Branch :

  • Show log

    Commit

  • Author : Antoine Labour
    Date : 2016-11-30 16:28:58
    Hash : 2ec65dc8
    Message : Handle GL_READ_BUFFER being GL_NONE in getReadAttachment() Change-Id: If3883de8f49a3fb33ecc598ba0b86c658cd3f0ca Reviewed-on: https://chromium-review.googlesource.com/415565 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>

  • src/libANGLE/Framebuffer.cpp
  • //
    // Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // Framebuffer.cpp: Implements the gl::Framebuffer class. Implements GL framebuffer
    // objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105.
    
    #include "libANGLE/Framebuffer.h"
    
    #include "common/Optional.h"
    #include "common/utilities.h"
    #include "libANGLE/Config.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/FramebufferAttachment.h"
    #include "libANGLE/Renderbuffer.h"
    #include "libANGLE/Surface.h"
    #include "libANGLE/Texture.h"
    #include "libANGLE/formatutils.h"
    #include "libANGLE/renderer/ContextImpl.h"
    #include "libANGLE/renderer/FramebufferImpl.h"
    #include "libANGLE/renderer/GLImplFactory.h"
    #include "libANGLE/renderer/RenderbufferImpl.h"
    #include "libANGLE/renderer/SurfaceImpl.h"
    
    using namespace angle;
    
    namespace gl
    {
    
    namespace
    {
    
    void BindResourceChannel(ChannelBinding *binding, FramebufferAttachmentObject *resource)
    {
        binding->bind(resource ? resource->getDirtyChannel() : nullptr);
    }
    
    }  // anonymous namespace
    
    FramebufferState::FramebufferState()
        : mLabel(),
          mColorAttachments(1),
          mDrawBufferStates(1, GL_NONE),
          mReadBufferState(GL_COLOR_ATTACHMENT0_EXT)
    {
        mDrawBufferStates[0] = GL_COLOR_ATTACHMENT0_EXT;
    }
    
    FramebufferState::FramebufferState(const Caps &caps)
        : mLabel(),
          mColorAttachments(caps.maxColorAttachments),
          mDrawBufferStates(caps.maxDrawBuffers, GL_NONE),
          mReadBufferState(GL_COLOR_ATTACHMENT0_EXT)
    {
        ASSERT(mDrawBufferStates.size() > 0);
        mDrawBufferStates[0] = GL_COLOR_ATTACHMENT0_EXT;
    }
    
    FramebufferState::~FramebufferState()
    {
    }
    
    const std::string &FramebufferState::getLabel()
    {
        return mLabel;
    }
    
    const FramebufferAttachment *FramebufferState::getAttachment(GLenum attachment) const
    {
        if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15)
        {
            return getColorAttachment(attachment - GL_COLOR_ATTACHMENT0);
        }
    
        switch (attachment)
        {
            case GL_COLOR:
            case GL_BACK:
                return getColorAttachment(0);
            case GL_DEPTH:
            case GL_DEPTH_ATTACHMENT:
                return getDepthAttachment();
            case GL_STENCIL:
            case GL_STENCIL_ATTACHMENT:
                return getStencilAttachment();
            case GL_DEPTH_STENCIL:
            case GL_DEPTH_STENCIL_ATTACHMENT:
                return getDepthStencilAttachment();
            default:
                UNREACHABLE();
                return nullptr;
        }
    }
    
    const FramebufferAttachment *FramebufferState::getReadAttachment() const
    {
        if (mReadBufferState == GL_NONE)
        {
            return nullptr;
        }
        ASSERT(mReadBufferState == GL_BACK || (mReadBufferState >= GL_COLOR_ATTACHMENT0 && mReadBufferState <= GL_COLOR_ATTACHMENT15));
        size_t readIndex = (mReadBufferState == GL_BACK ? 0 : static_cast<size_t>(mReadBufferState - GL_COLOR_ATTACHMENT0));
        ASSERT(readIndex < mColorAttachments.size());
        return mColorAttachments[readIndex].isAttached() ? &mColorAttachments[readIndex] : nullptr;
    }
    
    const FramebufferAttachment *FramebufferState::getFirstColorAttachment() const
    {
        for (const FramebufferAttachment &colorAttachment : mColorAttachments)
        {
            if (colorAttachment.isAttached())
            {
                return &colorAttachment;
            }
        }
    
        return nullptr;
    }
    
    const FramebufferAttachment *FramebufferState::getDepthOrStencilAttachment() const
    {
        if (mDepthAttachment.isAttached())
        {
            return &mDepthAttachment;
        }
        if (mStencilAttachment.isAttached())
        {
            return &mStencilAttachment;
        }
        return nullptr;
    }
    
    const FramebufferAttachment *FramebufferState::getColorAttachment(size_t colorAttachment) const
    {
        ASSERT(colorAttachment < mColorAttachments.size());
        return mColorAttachments[colorAttachment].isAttached() ?
               &mColorAttachments[colorAttachment] :
               nullptr;
    }
    
    const FramebufferAttachment *FramebufferState::getDepthAttachment() const
    {
        return mDepthAttachment.isAttached() ? &mDepthAttachment : nullptr;
    }
    
    const FramebufferAttachment *FramebufferState::getStencilAttachment() const
    {
        return mStencilAttachment.isAttached() ? &mStencilAttachment : nullptr;
    }
    
    const FramebufferAttachment *FramebufferState::getDepthStencilAttachment() const
    {
        // A valid depth-stencil attachment has the same resource bound to both the
        // depth and stencil attachment points.
        if (mDepthAttachment.isAttached() && mStencilAttachment.isAttached() &&
            mDepthAttachment == mStencilAttachment)
        {
            return &mDepthAttachment;
        }
    
        return nullptr;
    }
    
    bool FramebufferState::attachmentsHaveSameDimensions() const
    {
        Optional<Extents> attachmentSize;
    
        auto hasMismatchedSize = [&attachmentSize](const FramebufferAttachment &attachment)
        {
            if (!attachment.isAttached())
            {
                return false;
            }
    
            if (!attachmentSize.valid())
            {
                attachmentSize = attachment.getSize();
                return false;
            }
    
            return (attachment.getSize() != attachmentSize.value());
        };
    
        for (const auto &attachment : mColorAttachments)
        {
            if (hasMismatchedSize(attachment))
            {
                return false;
            }
        }
    
        if (hasMismatchedSize(mDepthAttachment))
        {
            return false;
        }
    
        return !hasMismatchedSize(mStencilAttachment);
    }
    
    const gl::FramebufferAttachment *FramebufferState::getDrawBuffer(size_t drawBufferIdx) const
    {
        ASSERT(drawBufferIdx < mDrawBufferStates.size());
        if (mDrawBufferStates[drawBufferIdx] != GL_NONE)
        {
            // ES3 spec: "If the GL is bound to a draw framebuffer object, the ith buffer listed in bufs
            // must be COLOR_ATTACHMENTi or NONE"
            ASSERT(mDrawBufferStates[drawBufferIdx] == GL_COLOR_ATTACHMENT0 + drawBufferIdx ||
                   (drawBufferIdx == 0 && mDrawBufferStates[drawBufferIdx] == GL_BACK));
            return getAttachment(mDrawBufferStates[drawBufferIdx]);
        }
        else
        {
            return nullptr;
        }
    }
    
    size_t FramebufferState::getDrawBufferCount() const
    {
        return mDrawBufferStates.size();
    }
    
    bool FramebufferState::colorAttachmentsAreUniqueImages() const
    {
        for (size_t firstAttachmentIdx = 0; firstAttachmentIdx < mColorAttachments.size();
             firstAttachmentIdx++)
        {
            const gl::FramebufferAttachment &firstAttachment = mColorAttachments[firstAttachmentIdx];
            if (!firstAttachment.isAttached())
            {
                continue;
            }
    
            for (size_t secondAttachmentIdx = firstAttachmentIdx + 1;
                 secondAttachmentIdx < mColorAttachments.size(); secondAttachmentIdx++)
            {
                const gl::FramebufferAttachment &secondAttachment =
                    mColorAttachments[secondAttachmentIdx];
                if (!secondAttachment.isAttached())
                {
                    continue;
                }
    
                if (firstAttachment == secondAttachment)
                {
                    return false;
                }
            }
        }
    
        return true;
    }
    
    Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, GLuint id)
        : mState(caps),
          mImpl(factory->createFramebuffer(mState)),
          mId(id),
          mCachedStatus(),
          mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
          mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
    {
        ASSERT(mId != 0);
        ASSERT(mImpl != nullptr);
        ASSERT(mState.mColorAttachments.size() == static_cast<size_t>(caps.maxColorAttachments));
    
        for (size_t colorIndex = 0; colorIndex < mState.mColorAttachments.size(); ++colorIndex)
        {
            mDirtyColorAttachmentBindings.push_back(ChannelBinding(
                this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex)));
        }
    }
    
    Framebuffer::Framebuffer(rx::SurfaceImpl *surface)
        : mState(),
          mImpl(surface->createDefaultFramebuffer(mState)),
          mId(0),
          mCachedStatus(GL_FRAMEBUFFER_COMPLETE),
          mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
          mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
    {
        ASSERT(mImpl != nullptr);
        mDirtyColorAttachmentBindings.push_back(
            ChannelBinding(this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0)));
    }
    
    Framebuffer::~Framebuffer()
    {
        SafeDelete(mImpl);
    }
    
    void Framebuffer::setLabel(const std::string &label)
    {
        mState.mLabel = label;
    }
    
    const std::string &Framebuffer::getLabel() const
    {
        return mState.mLabel;
    }
    
    void Framebuffer::detachTexture(GLuint textureId)
    {
        detachResourceById(GL_TEXTURE, textureId);
    }
    
    void Framebuffer::detachRenderbuffer(GLuint renderbufferId)
    {
        detachResourceById(GL_RENDERBUFFER, renderbufferId);
    }
    
    void Framebuffer::detachResourceById(GLenum resourceType, GLuint resourceId)
    {
        for (size_t colorIndex = 0; colorIndex < mState.mColorAttachments.size(); ++colorIndex)
        {
            detachMatchingAttachment(&mState.mColorAttachments[colorIndex], resourceType, resourceId,
                                     DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
        }
    
        detachMatchingAttachment(&mState.mDepthAttachment, resourceType, resourceId,
                                 DIRTY_BIT_DEPTH_ATTACHMENT);
        detachMatchingAttachment(&mState.mStencilAttachment, resourceType, resourceId,
                                 DIRTY_BIT_STENCIL_ATTACHMENT);
    }
    
    void Framebuffer::detachMatchingAttachment(FramebufferAttachment *attachment,
                                               GLenum matchType,
                                               GLuint matchId,
                                               size_t dirtyBit)
    {
        if (attachment->isAttached() && attachment->type() == matchType && attachment->id() == matchId)
        {
            attachment->detach();
            mDirtyBits.set(dirtyBit);
        }
    }
    
    const FramebufferAttachment *Framebuffer::getColorbuffer(size_t colorAttachment) const
    {
        return mState.getColorAttachment(colorAttachment);
    }
    
    const FramebufferAttachment *Framebuffer::getDepthbuffer() const
    {
        return mState.getDepthAttachment();
    }
    
    const FramebufferAttachment *Framebuffer::getStencilbuffer() const
    {
        return mState.getStencilAttachment();
    }
    
    const FramebufferAttachment *Framebuffer::getDepthStencilBuffer() const
    {
        return mState.getDepthStencilAttachment();
    }
    
    const FramebufferAttachment *Framebuffer::getDepthOrStencilbuffer() const
    {
        return mState.getDepthOrStencilAttachment();
    }
    
    const FramebufferAttachment *Framebuffer::getReadColorbuffer() const
    {
        return mState.getReadAttachment();
    }
    
    GLenum Framebuffer::getReadColorbufferType() const
    {
        const FramebufferAttachment *readAttachment = mState.getReadAttachment();
        return (readAttachment != nullptr ? readAttachment->type() : GL_NONE);
    }
    
    const FramebufferAttachment *Framebuffer::getFirstColorbuffer() const
    {
        return mState.getFirstColorAttachment();
    }
    
    const FramebufferAttachment *Framebuffer::getAttachment(GLenum attachment) const
    {
        return mState.getAttachment(attachment);
    }
    
    size_t Framebuffer::getDrawbufferStateCount() const
    {
        return mState.mDrawBufferStates.size();
    }
    
    GLenum Framebuffer::getDrawBufferState(size_t drawBuffer) const
    {
        ASSERT(drawBuffer < mState.mDrawBufferStates.size());
        return mState.mDrawBufferStates[drawBuffer];
    }
    
    const std::vector<GLenum> &Framebuffer::getDrawBufferStates() const
    {
        return mState.getDrawBufferStates();
    }
    
    void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers)
    {
        auto &drawStates = mState.mDrawBufferStates;
    
        ASSERT(count <= drawStates.size());
        std::copy(buffers, buffers + count, drawStates.begin());
        std::fill(drawStates.begin() + count, drawStates.end(), GL_NONE);
        mDirtyBits.set(DIRTY_BIT_DRAW_BUFFERS);
    }
    
    const FramebufferAttachment *Framebuffer::getDrawBuffer(size_t drawBuffer) const
    {
        return mState.getDrawBuffer(drawBuffer);
    }
    
    bool Framebuffer::hasEnabledDrawBuffer() const
    {
        for (size_t drawbufferIdx = 0; drawbufferIdx < mState.mDrawBufferStates.size(); ++drawbufferIdx)
        {
            if (getDrawBuffer(drawbufferIdx) != nullptr)
            {
                return true;
            }
        }
    
        return false;
    }
    
    GLenum Framebuffer::getReadBufferState() const
    {
        return mState.mReadBufferState;
    }
    
    void Framebuffer::setReadBuffer(GLenum buffer)
    {
        ASSERT(buffer == GL_BACK || buffer == GL_NONE ||
               (buffer >= GL_COLOR_ATTACHMENT0 &&
                (buffer - GL_COLOR_ATTACHMENT0) < mState.mColorAttachments.size()));
        mState.mReadBufferState = buffer;
        mDirtyBits.set(DIRTY_BIT_READ_BUFFER);
    }
    
    size_t Framebuffer::getNumColorBuffers() const
    {
        return mState.mColorAttachments.size();
    }
    
    bool Framebuffer::hasDepth() const
    {
        return (mState.mDepthAttachment.isAttached() && mState.mDepthAttachment.getDepthSize() > 0);
    }
    
    bool Framebuffer::hasStencil() const
    {
        return (mState.mStencilAttachment.isAttached() &&
                mState.mStencilAttachment.getStencilSize() > 0);
    }
    
    bool Framebuffer::usingExtendedDrawBuffers() const
    {
        for (size_t drawbufferIdx = 1; drawbufferIdx < mState.mDrawBufferStates.size(); ++drawbufferIdx)
        {
            if (getDrawBuffer(drawbufferIdx) != nullptr)
            {
                return true;
            }
        }
    
        return false;
    }
    
    GLenum Framebuffer::checkStatus(const ContextState &state)
    {
        // The default framebuffer *must* always be complete, though it may not be
        // subject to the same rules as application FBOs. ie, it could have 0x0 size.
        if (mId == 0)
        {
            return GL_FRAMEBUFFER_COMPLETE;
        }
    
        if (hasAnyDirtyBit() || !mCachedStatus.valid())
        {
            mCachedStatus = checkStatusImpl(state);
        }
    
        return mCachedStatus.value();
    }
    
    GLenum Framebuffer::checkStatusImpl(const ContextState &state)
    {
        ASSERT(mId != 0);
    
        unsigned int colorbufferSize = 0;
        int samples = -1;
        bool missingAttachment = true;
    
        for (const FramebufferAttachment &colorAttachment : mState.mColorAttachments)
        {
            if (colorAttachment.isAttached())
            {
                const Extents &size = colorAttachment.getSize();
                if (size.width == 0 || size.height == 0)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
    
                const Format &format          = colorAttachment.getFormat();
                const TextureCaps &formatCaps = state.getTextureCap(format.asSized());
                if (colorAttachment.type() == GL_TEXTURE)
                {
                    if (!formatCaps.renderable)
                    {
                        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                    }
    
                    if (format.info->depthBits > 0 || format.info->stencilBits > 0)
                    {
                        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                    }
    
                    if (colorAttachment.layer() >= size.depth)
                    {
                        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                    }
    
                    // ES3 specifies that cube map texture attachments must be cube complete.
                    // This language is missing from the ES2 spec, but we enforce it here because some
                    // desktop OpenGL drivers also enforce this validation.
                    // TODO(jmadill): Check if OpenGL ES2 drivers enforce cube completeness.
                    const Texture *texture = colorAttachment.getTexture();
                    ASSERT(texture);
                    if (texture->getTarget() == GL_TEXTURE_CUBE_MAP &&
                        !texture->getTextureState().isCubeComplete())
                    {
                        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                    }
                }
                else if (colorAttachment.type() == GL_RENDERBUFFER)
                {
                    if (!formatCaps.renderable || format.info->depthBits > 0 ||
                        format.info->stencilBits > 0)
                    {
                        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                    }
                }
    
                if (!missingAttachment)
                {
                    // APPLE_framebuffer_multisample, which EXT_draw_buffers refers to, requires that
                    // all color attachments have the same number of samples for the FBO to be complete.
                    if (colorAttachment.getSamples() != samples)
                    {
                        return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT;
                    }
    
                    // in GLES 2.0, all color attachments attachments must have the same number of bitplanes
                    // in GLES 3.0, there is no such restriction
                    if (state.getClientMajorVersion() < 3)
                    {
                        if (format.info->pixelBytes != colorbufferSize)
                        {
                            return GL_FRAMEBUFFER_UNSUPPORTED;
                        }
                    }
                }
                else
                {
                    samples = colorAttachment.getSamples();
                    colorbufferSize   = format.info->pixelBytes;
                    missingAttachment = false;
                }
            }
        }
    
        const FramebufferAttachment &depthAttachment = mState.mDepthAttachment;
        if (depthAttachment.isAttached())
        {
            const Extents &size = depthAttachment.getSize();
            if (size.width == 0 || size.height == 0)
            {
                return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
            }
    
            const Format &format          = depthAttachment.getFormat();
            const TextureCaps &formatCaps = state.getTextureCap(format.asSized());
            if (depthAttachment.type() == GL_TEXTURE)
            {
                // depth texture attachments require OES/ANGLE_depth_texture
                if (!state.getExtensions().depthTextures)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
    
                if (!formatCaps.renderable)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
    
                if (format.info->depthBits == 0)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
            }
            else if (depthAttachment.type() == GL_RENDERBUFFER)
            {
                if (!formatCaps.renderable || format.info->depthBits == 0)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
            }
    
            if (missingAttachment)
            {
                samples = depthAttachment.getSamples();
                missingAttachment = false;
            }
            else if (samples != depthAttachment.getSamples())
            {
                // CHROMIUM_framebuffer_mixed_samples allows a framebuffer to be
                // considered complete when its depth or stencil samples are a
                // multiple of the number of color samples.
                const bool mixedSamples = state.getExtensions().framebufferMixedSamples;
                if (!mixedSamples)
                    return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
    
                const int colorSamples = samples ? samples : 1;
                const int depthSamples = depthAttachment.getSamples();
                if ((depthSamples % colorSamples) != 0)
                    return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
            }
        }
    
        const FramebufferAttachment &stencilAttachment = mState.mStencilAttachment;
        if (stencilAttachment.isAttached())
        {
            const Extents &size = stencilAttachment.getSize();
            if (size.width == 0 || size.height == 0)
            {
                return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
            }
    
            const Format &format          = stencilAttachment.getFormat();
            const TextureCaps &formatCaps = state.getTextureCap(format.asSized());
            if (stencilAttachment.type() == GL_TEXTURE)
            {
                // texture stencil attachments come along as part
                // of OES_packed_depth_stencil + OES/ANGLE_depth_texture
                if (!state.getExtensions().depthTextures)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
    
                if (!formatCaps.renderable)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
    
                if (format.info->stencilBits == 0)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
            }
            else if (stencilAttachment.type() == GL_RENDERBUFFER)
            {
                if (!formatCaps.renderable || format.info->stencilBits == 0)
                {
                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                }
            }
    
            if (missingAttachment)
            {
                samples = stencilAttachment.getSamples();
                missingAttachment = false;
            }
            else if (samples != stencilAttachment.getSamples())
            {
                // see the comments in depth attachment check.
                const bool mixedSamples = state.getExtensions().framebufferMixedSamples;
                if (!mixedSamples)
                    return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
    
                const int colorSamples   = samples ? samples : 1;
                const int stencilSamples = stencilAttachment.getSamples();
                if ((stencilSamples % colorSamples) != 0)
                    return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
            }
    
            // Starting from ES 3.0 stencil and depth, if present, should be the same image
            if (state.getClientMajorVersion() >= 3 && depthAttachment.isAttached() &&
                stencilAttachment != depthAttachment)
            {
                return GL_FRAMEBUFFER_UNSUPPORTED;
            }
        }
    
        // we need to have at least one attachment to be complete
        if (missingAttachment)
        {
            return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
        }
    
        // In ES 2.0, all color attachments must have the same width and height.
        // In ES 3.0, there is no such restriction.
        if (state.getClientMajorVersion() < 3 && !mState.attachmentsHaveSameDimensions())
        {
            return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
        }
    
        syncState();
        if (!mImpl->checkStatus())
        {
            return GL_FRAMEBUFFER_UNSUPPORTED;
        }
    
        return GL_FRAMEBUFFER_COMPLETE;
    }
    
    Error Framebuffer::discard(size_t count, const GLenum *attachments)
    {
        return mImpl->discard(count, attachments);
    }
    
    Error Framebuffer::invalidate(size_t count, const GLenum *attachments)
    {
        return mImpl->invalidate(count, attachments);
    }
    
    Error Framebuffer::invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area)
    {
        return mImpl->invalidateSub(count, attachments, area);
    }
    
    Error Framebuffer::clear(rx::ContextImpl *context, GLbitfield mask)
    {
        if (context->getGLState().isRasterizerDiscardEnabled())
        {
            return gl::NoError();
        }
    
        return mImpl->clear(context, mask);
    }
    
    Error Framebuffer::clearBufferfv(rx::ContextImpl *context,
                                     GLenum buffer,
                                     GLint drawbuffer,
                                     const GLfloat *values)
    {
        if (context->getGLState().isRasterizerDiscardEnabled())
        {
            return gl::NoError();
        }
    
        return mImpl->clearBufferfv(context, buffer, drawbuffer, values);
    }
    
    Error Framebuffer::clearBufferuiv(rx::ContextImpl *context,
                                      GLenum buffer,
                                      GLint drawbuffer,
                                      const GLuint *values)
    {
        if (context->getGLState().isRasterizerDiscardEnabled())
        {
            return gl::NoError();
        }
    
        return mImpl->clearBufferuiv(context, buffer, drawbuffer, values);
    }
    
    Error Framebuffer::clearBufferiv(rx::ContextImpl *context,
                                     GLenum buffer,
                                     GLint drawbuffer,
                                     const GLint *values)
    {
        if (context->getGLState().isRasterizerDiscardEnabled())
        {
            return gl::NoError();
        }
    
        return mImpl->clearBufferiv(context, buffer, drawbuffer, values);
    }
    
    Error Framebuffer::clearBufferfi(rx::ContextImpl *context,
                                     GLenum buffer,
                                     GLint drawbuffer,
                                     GLfloat depth,
                                     GLint stencil)
    {
        if (context->getGLState().isRasterizerDiscardEnabled())
        {
            return gl::NoError();
        }
    
        return mImpl->clearBufferfi(context, buffer, drawbuffer, depth, stencil);
    }
    
    GLenum Framebuffer::getImplementationColorReadFormat() const
    {
        return mImpl->getImplementationColorReadFormat();
    }
    
    GLenum Framebuffer::getImplementationColorReadType() const
    {
        return mImpl->getImplementationColorReadType();
    }
    
    Error Framebuffer::readPixels(rx::ContextImpl *context,
                                  const Rectangle &area,
                                  GLenum format,
                                  GLenum type,
                                  GLvoid *pixels) const
    {
        ANGLE_TRY(mImpl->readPixels(context, area, format, type, pixels));
    
        Buffer *unpackBuffer = context->getGLState().getUnpackState().pixelBuffer.get();
        if (unpackBuffer)
        {
            unpackBuffer->onPixelUnpack();
        }
    
        return NoError();
    }
    
    Error Framebuffer::blit(rx::ContextImpl *context,
                            const Rectangle &sourceArea,
                            const Rectangle &destArea,
                            GLbitfield mask,
                            GLenum filter)
    {
        return mImpl->blit(context, sourceArea, destArea, mask, filter);
    }
    
    int Framebuffer::getSamples(const ContextState &state)
    {
        if (complete(state))
        {
            // For a complete framebuffer, all attachments must have the same sample count.
            // In this case return the first nonzero sample size.
            const auto *firstColorAttachment = mState.getFirstColorAttachment();
            if (firstColorAttachment)
            {
                ASSERT(firstColorAttachment->isAttached());
                return firstColorAttachment->getSamples();
            }
        }
    
        return 0;
    }
    
    bool Framebuffer::hasValidDepthStencil() const
    {
        return mState.getDepthStencilAttachment() != nullptr;
    }
    
    void Framebuffer::setAttachment(GLenum type,
                                    GLenum binding,
                                    const ImageIndex &textureIndex,
                                    FramebufferAttachmentObject *resource)
    {
        if (binding == GL_DEPTH_STENCIL || binding == GL_DEPTH_STENCIL_ATTACHMENT)
        {
            // ensure this is a legitimate depth+stencil format
            FramebufferAttachmentObject *attachmentObj = resource;
            if (resource)
            {
                FramebufferAttachment::Target target(binding, textureIndex);
                const Format &format = resource->getAttachmentFormat(target);
                if (format.info->depthBits == 0 || format.info->stencilBits == 0)
                {
                    // Attaching nullptr detaches the current attachment.
                    attachmentObj = nullptr;
                }
            }
    
            mState.mDepthAttachment.attach(type, binding, textureIndex, attachmentObj);
            mState.mStencilAttachment.attach(type, binding, textureIndex, attachmentObj);
            mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
            mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
            BindResourceChannel(&mDirtyDepthAttachmentBinding, resource);
            BindResourceChannel(&mDirtyStencilAttachmentBinding, resource);
        }
        else
        {
            switch (binding)
            {
                case GL_DEPTH:
                case GL_DEPTH_ATTACHMENT:
                    mState.mDepthAttachment.attach(type, binding, textureIndex, resource);
                    mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
                    BindResourceChannel(&mDirtyDepthAttachmentBinding, resource);
                    break;
                case GL_STENCIL:
                case GL_STENCIL_ATTACHMENT:
                    mState.mStencilAttachment.attach(type, binding, textureIndex, resource);
                    mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
                    BindResourceChannel(&mDirtyStencilAttachmentBinding, resource);
                    break;
                case GL_BACK:
                    mState.mColorAttachments[0].attach(type, binding, textureIndex, resource);
                    mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0);
                    // No need for a resource binding for the default FBO, it's always complete.
                    break;
                default:
                {
                    size_t colorIndex = binding - GL_COLOR_ATTACHMENT0;
                    ASSERT(colorIndex < mState.mColorAttachments.size());
                    mState.mColorAttachments[colorIndex].attach(type, binding, textureIndex, resource);
                    mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
                    BindResourceChannel(&mDirtyColorAttachmentBindings[colorIndex], resource);
                }
                break;
            }
        }
    }
    
    void Framebuffer::resetAttachment(GLenum binding)
    {
        setAttachment(GL_NONE, binding, ImageIndex::MakeInvalid(), nullptr);
    }
    
    void Framebuffer::syncState()
    {
        if (mDirtyBits.any())
        {
            mImpl->syncState(mDirtyBits);
            mDirtyBits.reset();
            mCachedStatus.reset();
        }
    }
    
    void Framebuffer::signal(SignalToken token)
    {
        // TOOD(jmadill): Make this only update individual attachments to do less work.
        mCachedStatus.reset();
    }
    
    bool Framebuffer::complete(const ContextState &state)
    {
        return (checkStatus(state) == GL_FRAMEBUFFER_COMPLETE);
    }
    
    }  // namespace gl