Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Framebuffer.cpp   Sprache: C

 
//
// Copyright 2002 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/bitset_utils.h"
#include "common/utilities.h"
#include "libANGLE/Config.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/PixelLocalStorage.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/Surface.h"
#include "libANGLE/Texture.h"
#include "libANGLE/angletypes.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
{

// Check the |checkAttachment| in reference to |firstAttachment| for the sake of multiview
// framebuffer completeness.
FramebufferStatus CheckMultiviewStateMatchesForCompleteness(
    const FramebufferAttachment *firstAttachment,
    const FramebufferAttachment *checkAttachment)
{
    ASSERT(firstAttachment && checkAttachment);
    ASSERT(firstAttachment->isAttached() && checkAttachment->isAttached());

    if (firstAttachment->isMultiview() != checkAttachment->isMultiview())
    {
        return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR,
                                             err::kFramebufferIncompleteMultiviewMismatch);
    }
    if (firstAttachment->getNumViews() != checkAttachment->getNumViews())
    {
        return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR,
                                             err::kFramebufferIncompleteMultiviewViewsMismatch);
    }
    if (checkAttachment->getBaseViewIndex() + checkAttachment->getNumViews() >
        checkAttachment->getSize().depth)
    {
        return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR,
                                             err::kFramebufferIncompleteMultiviewBaseViewMismatch);
    }

    return FramebufferStatus::Complete();
}

FramebufferStatus CheckAttachmentCompleteness(const Context *context,
                                              const FramebufferAttachment &attachment)
{
    ASSERT(attachment.isAttached());

    const Extents &size = attachment.getSize();
    if (size.width == 0 || size.height == 0)
    {
        return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                                             err::kFramebufferIncompleteAttachmentZeroSize);
    }

    if (!attachment.isRenderable(context))
    {
        return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                                             err::kFramebufferIncompleteAttachmentNotRenderable);
    }

    if (attachment.type() == GL_TEXTURE)
    {
        // [EXT_geometry_shader] Section 9.4.1, "Framebuffer Completeness"
        // If <image> is a three-dimensional texture or a two-dimensional array texture and the
        // attachment is not layered, the selected layer is less than the depth or layer count,
        // respectively, of the texture.
        if (!attachment.isLayered())
        {
            if (attachment.layer() >= size.depth)
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                    err::kFramebufferIncompleteAttachmentLayerGreaterThanDepth);
            }
        }
        // If <image> is a three-dimensional texture or a two-dimensional array texture and the
        // attachment is layered, the depth or layer count, respectively, of the texture is less
        // than or equal to the value of MAX_FRAMEBUFFER_LAYERS_EXT.
        else
        {
            if (size.depth >= context->getCaps().maxFramebufferLayers)
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                    err::kFramebufferIncompleteAttachmentDepthGreaterThanMaxLayers);
            }
        }

        // 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 = attachment.getTexture();
        ASSERT(texture);
        if (texture->getType() == TextureType::CubeMap &&
            !texture->getTextureState().isCubeComplete())
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                err::kFramebufferIncompleteAttachmentNotCubeComplete);
        }

        if (!texture->getImmutableFormat())
        {
            GLuint attachmentMipLevel = static_cast<GLuint>(attachment.mipLevel());

            // From the ES 3.0 spec, pg 213:
            // If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE and the value of
            // FRAMEBUFFER_ATTACHMENT_OBJECT_NAME does not name an immutable-format texture,
            // then the value of FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL must be in the
            // range[levelbase, q], where levelbase is the value of TEXTURE_BASE_LEVEL and q is
            // the effective maximum texture level defined in the Mipmapping discussion of
            // section 3.8.10.4.
            if (attachmentMipLevel < texture->getBaseLevel() ||
                attachmentMipLevel > texture->getMipmapMaxLevel())
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                    err::kFramebufferIncompleteAttachmentLevelOutOfBaseMaxLevelRange);
            }

            // Form the ES 3.0 spec, pg 213/214:
            // If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE and the value of
            // FRAMEBUFFER_ATTACHMENT_OBJECT_NAME does not name an immutable-format texture and
            // the value of FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL is not levelbase, then the
            // texture must be mipmap complete, and if FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names
            // a cubemap texture, the texture must also be cube complete.
            if (attachmentMipLevel != texture->getBaseLevel() && !texture->isMipmapComplete())
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                    err::kFramebufferIncompleteAttachmentLevelNotBaseLevelForIncompleteMipTexture);
            }
        }
    }

    return FramebufferStatus::Complete();
}

FramebufferStatus CheckAttachmentSampleCounts(const Context *context,
                                              GLsizei currAttachmentSamples,
                                              GLsizei samples,
                                              bool colorAttachment)
{
    if (currAttachmentSamples != samples)
    {
        if (colorAttachment)
        {
            // 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.
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                err::kFramebufferIncompleteMultisampleInconsistentSampleCounts);
        }
        else
        {
            // 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.
            if (!context->getExtensions().framebufferMixedSamplesCHROMIUM)
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                    err::kFramebufferIncompleteMultisampleInconsistentSampleCounts);
            }

            if ((currAttachmentSamples % std::max(samples, 1)) != 0)
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                    err::
                        kFramebufferIncompleteMultisampleDepthStencilSampleCountDivisibleByColorSampleCount);
            }
        }
    }

    return FramebufferStatus::Complete();
}

FramebufferStatus CheckAttachmentSampleCompleteness(const Context *context,
                                                    const FramebufferAttachment &attachment,
                                                    bool colorAttachment,
                                                    Optional<int> *samples,
                                                    Optional<bool> *fixedSampleLocations,
                                                    Optional<int> *renderToTextureSamples)
{
    ASSERT(attachment.isAttached());

    if (attachment.type() == GL_TEXTURE)
    {
        const Texture *texture = attachment.getTexture();
        ASSERT(texture);
        GLenum sizedInternalFormat    = attachment.getFormat().info->sizedInternalFormat;
        const TextureCaps &formatCaps = context->getTextureCaps().get(sizedInternalFormat);
        if (static_cast<GLuint>(attachment.getSamples()) > formatCaps.getMaxSamples())
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                err::kFramebufferIncompleteAttachmentSamplesGreaterThanMaxSupportedSamples);
        }

        const ImageIndex &attachmentImageIndex = attachment.getTextureImageIndex();
        bool fixedSampleloc = texture->getAttachmentFixedSampleLocations(attachmentImageIndex);
        if (fixedSampleLocations->valid() && fixedSampleloc != fixedSampleLocations->value())
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                err::kFramebufferIncompleteMultisampleInconsistentFixedSampleLocations);
        }
        else
        {
            *fixedSampleLocations = fixedSampleloc;
        }
    }

    if (renderToTextureSamples->valid())
    {
        // Only check against RenderToTextureSamples if they actually exist.
        if (renderToTextureSamples->value() !=
            FramebufferAttachment::kDefaultRenderToTextureSamples)
        {
            FramebufferStatus sampleCountStatus =
                CheckAttachmentSampleCounts(context, attachment.getRenderToTextureSamples(),
                                            renderToTextureSamples->value(), colorAttachment);
            if (!sampleCountStatus.isComplete())
            {
                return sampleCountStatus;
            }
        }
    }
    else
    {
        *renderToTextureSamples = attachment.getRenderToTextureSamples();
    }

    if (samples->valid())
    {
        // RenderToTextureSamples takes precedence if they exist.
        if (renderToTextureSamples->value() ==
            FramebufferAttachment::kDefaultRenderToTextureSamples)
        {

            FramebufferStatus sampleCountStatus = CheckAttachmentSampleCounts(
                context, attachment.getSamples(), samples->value(), colorAttachment);
            if (!sampleCountStatus.isComplete())
            {
                return sampleCountStatus;
            }
        }
    }
    else
    {
        *samples = attachment.getSamples();
    }

    return FramebufferStatus::Complete();
}

// Needed to index into the attachment arrays/bitsets.
static_assert(static_cast<size_t>(IMPLEMENTATION_MAX_DRAW_BUFFERS) ==
                  Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX,
              "Framebuffer Dirty bit mismatch");
static_assert(static_cast<size_t>(IMPLEMENTATION_MAX_DRAW_BUFFERS) ==
                  Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT,
              "Framebuffer Dirty bit mismatch");
static_assert(static_cast<size_t>(IMPLEMENTATION_MAX_DRAW_BUFFERS + 1) ==
                  Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT,
              "Framebuffer Dirty bit mismatch");

angle::Result InitAttachment(const Context *context, FramebufferAttachment *attachment)
{
    ASSERT(attachment->isAttached());
    if (attachment->initState() == InitState::MayNeedInit)
    {
        ANGLE_TRY(attachment->initializeContents(context));
    }
    return angle::Result::Continue;
}

bool AttachmentOverlapsWithTexture(const FramebufferAttachment &attachment,
                                   const Texture *texture,
                                   const Sampler *sampler)
{
    if (!attachment.isTextureWithId(texture->id()))
    {
        return false;
    }

    const gl::ImageIndex &index      = attachment.getTextureImageIndex();
    GLuint attachmentLevel           = static_cast<GLuint>(index.getLevelIndex());
    GLuint textureEffectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
    GLuint textureMaxLevel           = textureEffectiveBaseLevel;
    if ((sampler && IsMipmapFiltered(sampler->getSamplerState().getMinFilter())) ||
        IsMipmapFiltered(texture->getSamplerState().getMinFilter()))
    {
        textureMaxLevel = texture->getMipmapMaxLevel();
    }

    return attachmentLevel >= textureEffectiveBaseLevel && attachmentLevel <= textureMaxLevel;
}

}  // anonymous namespace

bool FramebufferStatus::isComplete() const
{
    return status == GL_FRAMEBUFFER_COMPLETE;
}

FramebufferStatus FramebufferStatus::Complete()
{
    FramebufferStatus result;
    result.status = GL_FRAMEBUFFER_COMPLETE;
    result.reason = nullptr;
    return result;
}

FramebufferStatus FramebufferStatus::Incomplete(GLenum status, const char *reason)
{
    ASSERT(status != GL_FRAMEBUFFER_COMPLETE);

    FramebufferStatus result;
    result.status = status;
    result.reason = reason;
    return result;
}

// This constructor is only used for default framebuffers.
FramebufferState::FramebufferState(rx::Serial serial)
    : mId(Framebuffer::kDefaultDrawFramebufferHandle),
      mFramebufferSerial(serial),
      mLabel(),
      mColorAttachments(1),
      mColorAttachmentsMask(0),
      mDrawBufferStates(1, GL_BACK),
      mReadBufferState(GL_BACK),
      mDrawBufferTypeMask(),
      mDefaultWidth(0),
      mDefaultHeight(0),
      mDefaultSamples(0),
      mDefaultFixedSampleLocations(GL_FALSE),
      mDefaultLayers(0),
      mFlipY(GL_FALSE),
      mWebGLDepthStencilConsistent(true),
      mDefaultFramebufferReadAttachmentInitialized(false),
      mSrgbWriteControlMode(SrgbWriteControlMode::Default)
{
    ASSERT(mDrawBufferStates.size() > 0);
    mEnabledDrawBuffers.set(0);
}

FramebufferState::FramebufferState(const Caps &caps, FramebufferID id, rx::Serial serial)
    : mId(id),
      mFramebufferSerial(serial),
      mLabel(),
      mColorAttachments(caps.maxColorAttachments),
      mColorAttachmentsMask(0),
      mDrawBufferStates(caps.maxDrawBuffers, GL_NONE),
      mReadBufferState(GL_COLOR_ATTACHMENT0_EXT),
      mDrawBufferTypeMask(),
      mDefaultWidth(0),
      mDefaultHeight(0),
      mDefaultSamples(0),
      mDefaultFixedSampleLocations(GL_FALSE),
      mDefaultLayers(0),
      mFlipY(GL_FALSE),
      mWebGLDepthStencilConsistent(true),
      mDefaultFramebufferReadAttachmentInitialized(false),
      mSrgbWriteControlMode(SrgbWriteControlMode::Default)
{
    ASSERT(mId != Framebuffer::kDefaultDrawFramebufferHandle);
    ASSERT(mDrawBufferStates.size() > 0);
    mDrawBufferStates[0] = GL_COLOR_ATTACHMENT0_EXT;
}

FramebufferState::~FramebufferState() {}

const std::string &FramebufferState::getLabel() const
{
    return mLabel;
}

const FramebufferAttachment *FramebufferState::getAttachment(const Context *context,
                                                             GLenum attachment) const
{
    if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15)
    {
        return getColorAttachment(attachment - GL_COLOR_ATTACHMENT0);
    }

    // WebGL1 allows a developer to query for attachment parameters even when "inconsistant" (i.e.
    // multiple conflicting attachment points) and requires us to return the framebuffer attachment
    // associated with WebGL.
    switch (attachment)
    {
        case GL_COLOR:
        case GL_BACK:
            return getColorAttachment(0);
        case GL_DEPTH:
        case GL_DEPTH_ATTACHMENT:
            if (context->isWebGL1())
            {
                return getWebGLDepthAttachment();
            }
            else
            {
                return getDepthAttachment();
            }
        case GL_STENCIL:
        case GL_STENCIL_ATTACHMENT:
            if (context->isWebGL1())
            {
                return getWebGLStencilAttachment();
            }
            else
            {
                return getStencilAttachment();
            }
        case GL_DEPTH_STENCIL:
        case GL_DEPTH_STENCIL_ATTACHMENT:
            if (context->isWebGL1())
            {
                return getWebGLDepthStencilAttachment();
            }
            else
            {
                return getDepthStencilAttachment();
            }
        default:
            UNREACHABLE();
            return nullptr;
    }
}

uint32_t FramebufferState::getReadIndex() const
{
    ASSERT(mReadBufferState == GL_BACK ||
           (mReadBufferState >= GL_COLOR_ATTACHMENT0 && mReadBufferState <= GL_COLOR_ATTACHMENT15));
    uint32_t readIndex = mReadBufferState == GL_BACK ? 0 : mReadBufferState - GL_COLOR_ATTACHMENT0;
    ASSERT(readIndex < mColorAttachments.size());
    return readIndex;
}

const FramebufferAttachment *FramebufferState::getReadAttachment() const
{
    if (mReadBufferState == GL_NONE)
    {
        return nullptr;
    }

    uint32_t readIndex = getReadIndex();
    const gl::FramebufferAttachment &framebufferAttachment =
        isDefault() ? mDefaultFramebufferReadAttachment : mColorAttachments[readIndex];

    return framebufferAttachment.isAttached() ? &framebufferAttachment : nullptr;
}

const FramebufferAttachment *FramebufferState::getReadPixelsAttachment(GLenum readFormat) const
{
    switch (readFormat)
    {
        case GL_DEPTH_COMPONENT:
            return getDepthAttachment();
        case GL_STENCIL_INDEX_OES:
            return getStencilOrDepthStencilAttachment();
        case GL_DEPTH_STENCIL_OES:
            return getDepthStencilAttachment();
        default:
            return getReadAttachment();
    }
}

const FramebufferAttachment *FramebufferState::getFirstNonNullAttachment() const
{
    auto *colorAttachment = getFirstColorAttachment();
    if (colorAttachment)
    {
        return colorAttachment;
    }
    return getDepthOrStencilAttachment();
}

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::getStencilOrDepthStencilAttachment(const
{
    if (mStencilAttachment.isAttached())
    {
        return &mStencilAttachment;
    }
    return getDepthStencilAttachment();
}

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::getWebGLDepthAttachment() const
{
    return mWebGLDepthAttachment.isAttached() ? &mWebGLDepthAttachment : nullptr;
}

const FramebufferAttachment *FramebufferState::getWebGLDepthStencilAttachment() const
{
    return mWebGLDepthStencilAttachment.isAttached() ? &mWebGLDepthStencilAttachment : nullptr;
}

const FramebufferAttachment *FramebufferState::getStencilAttachment() const
{
    return mStencilAttachment.isAttached() ? &mStencilAttachment : nullptr;
}

const FramebufferAttachment *FramebufferState::getWebGLStencilAttachment() const
{
    return mWebGLStencilAttachment.isAttached() ? &mWebGLStencilAttachment : 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;
}

const Extents FramebufferState::getAttachmentExtentsIntersection() const
{
    int32_t width  = std::numeric_limits<int32_t>::max();
    int32_t height = std::numeric_limits<int32_t>::max();
    for (const FramebufferAttachment &attachment : mColorAttachments)
    {
        if (attachment.isAttached())
        {
            width  = std::min(width, attachment.getSize().width);
            height = std::min(height, attachment.getSize().height);
        }
    }

    if (mDepthAttachment.isAttached())
    {
        width  = std::min(width, mDepthAttachment.getSize().width);
        height = std::min(height, mDepthAttachment.getSize().height);
    }

    if (mStencilAttachment.isAttached())
    {
        width  = std::min(width, mStencilAttachment.getSize().width);
        height = std::min(height, mStencilAttachment.getSize().height);
    }

    return Extents(width, height, 0);
}

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;
        }

        const auto &prevSize = attachmentSize.value();
        const auto &curSize  = attachment.getSize();
        return (curSize.width != prevSize.width || curSize.height != prevSize.height);
    };

    for (const auto &attachment : mColorAttachments)
    {
        if (hasMismatchedSize(attachment))
        {
            return false;
        }
    }

    if (hasMismatchedSize(mDepthAttachment))
    {
        return false;
    }

    return !hasMismatchedSize(mStencilAttachment);
}

bool FramebufferState::hasSeparateDepthAndStencilAttachments() const
{
    // if we have both a depth and stencil buffer, they must refer to the same object
    // since we only support packed_depth_stencil and not separate depth and stencil
    return (getDepthAttachment() != nullptr && getStencilAttachment() != nullptr &&
            getDepthStencilAttachment() == nullptr);
}

const 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));

        if (mDrawBufferStates[drawBufferIdx] == GL_BACK)
        {
            return getColorAttachment(0);
        }
        else
        {
            return getColorAttachment(mDrawBufferStates[drawBufferIdx] - GL_COLOR_ATTACHMENT0);
        }
    }
    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 FramebufferAttachment &firstAttachment = mColorAttachments[firstAttachmentIdx];
        if (!firstAttachment.isAttached())
        {
            continue;
        }

        for (size_t secondAttachmentIdx = firstAttachmentIdx + 1;
             secondAttachmentIdx < mColorAttachments.size(); secondAttachmentIdx++)
        {
            const FramebufferAttachment &secondAttachment = mColorAttachments[secondAttachmentIdx];
            if (!secondAttachment.isAttached())
            {
                continue;
            }

            if (firstAttachment == secondAttachment)
            {
                return false;
            }
        }
    }

    return true;
}

bool FramebufferState::hasDepth() const
{
    return (mDepthAttachment.isAttached() && mDepthAttachment.getDepthSize() > 0);
}

bool FramebufferState::hasStencil() const
{
    return (mStencilAttachment.isAttached() && mStencilAttachment.getStencilSize() > 0);
}

bool FramebufferState::hasExternalTextureAttachment() const
{
    // External textures can only be bound to color attachment 0
    return (mColorAttachments[0].isAttached() && mColorAttachments[0].isExternalTexture());
}

bool FramebufferState::hasYUVAttachment() const
{
    // The only attachments that can be YUV are external textures and surfaces, both are attached at
    // color attachment 0.
    return (mColorAttachments[0].isAttached() && mColorAttachments[0].isYUV());
}

bool FramebufferState::isMultiview() const
{
    const FramebufferAttachment *attachment = getFirstNonNullAttachment();
    if (attachment == nullptr)
    {
        return false;
    }
    return attachment->isMultiview();
}

int FramebufferState::getBaseViewIndex() const
{
    const FramebufferAttachment *attachment = getFirstNonNullAttachment();
    if (attachment == nullptr)
    {
        return GL_NONE;
    }
    return attachment->getBaseViewIndex();
}

Box FramebufferState::getDimensions() const
{
    Extents extents = getExtents();
    return Box(0, 0, 0, extents.width, extents.height, extents.depth);
}

Extents FramebufferState::getExtents() const
{
    // OpenGLES3.0 (https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf
    // section 4.4.4.2) allows attachments have unequal size.
    const FramebufferAttachment *first = getFirstNonNullAttachment();
    if (first)
    {
        return getAttachmentExtentsIntersection();
    }
    return Extents(getDefaultWidth(), getDefaultHeight(), 0);
}

bool FramebufferState::isDefault() const
{
    return mId == Framebuffer::kDefaultDrawFramebufferHandle;
}

bool FramebufferState::isBoundAsDrawFramebuffer(const Context *context) const
{
    return context->getState().getDrawFramebuffer()->id() == mId;
}

const FramebufferID Framebuffer::kDefaultDrawFramebufferHandle = {0};

Framebuffer::Framebuffer(const Context *context, rx::GLImplFactory *factory)
    : mState(context->getShareGroup()->generateFramebufferSerial()),
      mImpl(factory->createFramebuffer(mState)),
      mCachedStatus(FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNDEFINED_OES,
                                                  err::kFramebufferIncompleteSurfaceless)),
      mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
      mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{
    mDirtyColorAttachmentBindings.emplace_back(this, DIRTY_BIT_COLOR_ATTACHMENT_0);
    SetComponentTypeMask(getDrawbufferWriteType(0), 0, &mState.mDrawBufferTypeMask);
}

Framebuffer::Framebuffer(const Context *context, rx::GLImplFactory *factory, FramebufferID id)
    : mState(context->getCaps(), id, context->getShareGroup()->generateFramebufferSerial()),
      mImpl(factory->createFramebuffer(mState)),
      mCachedStatus(),
      mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
      mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{
    ASSERT(mImpl != nullptr);
    ASSERT(mState.mColorAttachments.size() ==
           static_cast<size_t>(context->getCaps().maxColorAttachments));

    for (uint32_t colorIndex = 0;
         colorIndex < static_cast<uint32_t>(mState.mColorAttachments.size()); ++colorIndex)
    {
        mDirtyColorAttachmentBindings.emplace_back(this, DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
    }
    if (context->getClientVersion() >= ES_3_0)
    {
        mDirtyBits.set(DIRTY_BIT_READ_BUFFER);
    }
}

Framebuffer::~Framebuffer()
{
    SafeDelete(mImpl);
}

void Framebuffer::onDestroy(const Context *context)
{
    if (isDefault())
    {
        std::ignore = unsetSurfaces(context);
    }

    for (auto &attachment : mState.mColorAttachments)
    {
        attachment.detach(context, mState.mFramebufferSerial);
    }
    mState.mDepthAttachment.detach(context, mState.mFramebufferSerial);
    mState.mStencilAttachment.detach(context, mState.mFramebufferSerial);
    mState.mWebGLDepthAttachment.detach(context, mState.mFramebufferSerial);
    mState.mWebGLStencilAttachment.detach(context, mState.mFramebufferSerial);
    mState.mWebGLDepthStencilAttachment.detach(context, mState.mFramebufferSerial);

    if (mPixelLocalStorage)
    {
        mPixelLocalStorage->onFramebufferDestroyed(context);
    }

    mImpl->destroy(context);
}

egl::Error Framebuffer::setSurfaces(const Context *context,
                                    egl::Surface *surface,
                                    egl::Surface *readSurface)
{
    // This has to be a default framebuffer.
    ASSERT(isDefault());
    ASSERT(mDirtyColorAttachmentBindings.size() == 1);
    ASSERT(mDirtyColorAttachmentBindings[0].getSubjectIndex() == DIRTY_BIT_COLOR_ATTACHMENT_0);

    ASSERT(!mState.mColorAttachments[0].isAttached());
    ASSERT(!mState.mDepthAttachment.isAttached());
    ASSERT(!mState.mStencilAttachment.isAttached());

    if (surface)
    {
        setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_BACK, ImageIndex(), surface,
                          FramebufferAttachment::kDefaultNumViews,
                          FramebufferAttachment::kDefaultBaseViewIndex, false,
                          FramebufferAttachment::kDefaultRenderToTextureSamples);
        mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0);

        if (surface->getConfig()->depthSize > 0)
        {
            setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_DEPTH, ImageIndex(), surface,
                              FramebufferAttachment::kDefaultNumViews,
                              FramebufferAttachment::kDefaultBaseViewIndex, false,
                              FramebufferAttachment::kDefaultRenderToTextureSamples);
            mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
        }

        if (surface->getConfig()->stencilSize > 0)
        {
            setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_STENCIL, ImageIndex(), surface,
                              FramebufferAttachment::kDefaultNumViews,
                              FramebufferAttachment::kDefaultBaseViewIndex, false,
                              FramebufferAttachment::kDefaultRenderToTextureSamples);
            mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
        }

        mState.mSurfaceTextureOffset = surface->getTextureOffset();

        // Ensure the backend has a chance to synchronize its content for a new backbuffer.
        mDirtyBits.set(DIRTY_BIT_COLOR_BUFFER_CONTENTS_0);
    }

    setReadSurface(context, readSurface);

    SetComponentTypeMask(getDrawbufferWriteType(0), 0, &mState.mDrawBufferTypeMask);

    ASSERT(mCachedStatus.value().status == GL_FRAMEBUFFER_UNDEFINED_OES);
    ASSERT(mCachedStatus.value().reason == err::kFramebufferIncompleteSurfaceless);
    if (surface)
    {
        mCachedStatus = FramebufferStatus::Complete();
        ANGLE_TRY(surface->getImplementation()->attachToFramebuffer(context, this));
    }

    return egl::NoError();
}

void Framebuffer::setReadSurface(const Context *context, egl::Surface *readSurface)
{
    // This has to be a default framebuffer.
    ASSERT(isDefault());
    ASSERT(mDirtyColorAttachmentBindings.size() == 1);
    ASSERT(mDirtyColorAttachmentBindings[0].getSubjectIndex() == DIRTY_BIT_COLOR_ATTACHMENT_0);

    // Read surface is not attached.
    ASSERT(!mState.mDefaultFramebufferReadAttachment.isAttached());

    // updateAttachment() without mState.mResourceNeedsInit.set()
    mState.mDefaultFramebufferReadAttachment.attach(
        context, GL_FRAMEBUFFER_DEFAULT, GL_BACK, ImageIndex(), readSurface,
        FramebufferAttachment::kDefaultNumViews, FramebufferAttachment::kDefaultBaseViewIndex,
        false, FramebufferAttachment::kDefaultRenderToTextureSamples, mState.mFramebufferSerial);

    if (context->getClientVersion() >= ES_3_0)
    {
        mDirtyBits.set(DIRTY_BIT_READ_BUFFER);
    }
}

egl::Error Framebuffer::unsetSurfaces(const Context *context)
{
    // This has to be a default framebuffer.
    ASSERT(isDefault());
    ASSERT(mDirtyColorAttachmentBindings.size() == 1);
    ASSERT(mDirtyColorAttachmentBindings[0].getSubjectIndex() == DIRTY_BIT_COLOR_ATTACHMENT_0);

    if (mState.mColorAttachments[0].isAttached())
    {
        const egl::Surface *surface = mState.mColorAttachments[0].getSurface();
        mState.mColorAttachments[0].detach(context, mState.mFramebufferSerial);
        mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0);

        if (mState.mDepthAttachment.isAttached())
        {
            mState.mDepthAttachment.detach(context, mState.mFramebufferSerial);
            mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
        }

        if (mState.mStencilAttachment.isAttached())
        {
            mState.mStencilAttachment.detach(context, mState.mFramebufferSerial);
            mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
        }

        ANGLE_TRY(surface->getImplementation()->detachFromFramebuffer(context, this));

        ASSERT(mCachedStatus.value().status == GL_FRAMEBUFFER_COMPLETE);
        mCachedStatus = FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNDEFINED_OES,
                                                      err::kFramebufferIncompleteSurfaceless);
    }
    else
    {
        ASSERT(!mState.mDepthAttachment.isAttached());
        ASSERT(!mState.mStencilAttachment.isAttached());
        ASSERT(mCachedStatus.value().status == GL_FRAMEBUFFER_UNDEFINED_OES);
        ASSERT(mCachedStatus.value().reason == err::kFramebufferIncompleteSurfaceless);
    }

    mState.mDefaultFramebufferReadAttachment.detach(context, mState.mFramebufferSerial);
    mState.mDefaultFramebufferReadAttachmentInitialized = false;
    return egl::NoError();
}

angle::Result Framebuffer::setLabel(const Context *context, const std::string &label)
{
    mState.mLabel = label;

    if (mImpl)
    {
        return mImpl->onLabelUpdate(context);
    }
    return angle::Result::Continue;
}

const std::string &Framebuffer::getLabel() const
{
    return mState.mLabel;
}

bool Framebuffer::detachTexture(const Context *context, TextureID textureId)
{
    return detachResourceById(context, GL_TEXTURE, textureId.value);
}

bool Framebuffer::detachRenderbuffer(const Context *context, RenderbufferID renderbufferId)
{
    return detachResourceById(context, GL_RENDERBUFFER, renderbufferId.value);
}

bool Framebuffer::detachResourceById(const Context *context, GLenum resourceType, GLuint resourceId)
{
    bool found = false;

    for (size_t colorIndex = 0; colorIndex < mState.mColorAttachments.size(); ++colorIndex)
    {
        if (detachMatchingAttachment(context, &mState.mColorAttachments[colorIndex], resourceType,
                                     resourceId))
        {
            found = true;
        }
    }

    if (context->isWebGL1())
    {
        const std::array<FramebufferAttachment *, 3> attachments = {
            {&mState.mWebGLDepthStencilAttachment, &mState.mWebGLDepthAttachment,
             &mState.mWebGLStencilAttachment}};
        for (FramebufferAttachment *attachment : attachments)
        {
            if (detachMatchingAttachment(context, attachment, resourceType, resourceId))
            {
                found = true;
            }
        }
    }
    else
    {
        if (detachMatchingAttachment(context, &mState.mDepthAttachment, resourceType, resourceId))
        {
            found = true;
        }
        if (detachMatchingAttachment(context, &mState.mStencilAttachment, resourceType, resourceId))
        {
            found = true;
        }
    }

    return found;
}

bool Framebuffer::detachMatchingAttachment(const Context *context,
                                           FramebufferAttachment *attachment,
                                           GLenum matchType,
                                           GLuint matchId)
{
    if (attachment->isAttached() && attachment->type() == matchType && attachment->id() == matchId)
    {
        // We go through resetAttachment to make sure that all the required bookkeeping will be done
        // such as updating enabled draw buffer state.
        resetAttachment(context, attachment->getBinding());
        return true;
    }

    return false;
}

const FramebufferAttachment *Framebuffer::getColorAttachment(size_t colorAttachmentconst
{
    return mState.getColorAttachment(colorAttachment);
}

const FramebufferAttachment *Framebuffer::getDepthAttachment() const
{
    return mState.getDepthAttachment();
}

const FramebufferAttachment *Framebuffer::getStencilAttachment() const
{
    return mState.getStencilAttachment();
}

const FramebufferAttachment *Framebuffer::getDepthStencilAttachment() const
{
    return mState.getDepthStencilAttachment();
}

const FramebufferAttachment *Framebuffer::getDepthOrStencilAttachment() const
{
    return mState.getDepthOrStencilAttachment();
}

const FramebufferAttachment *Framebuffer::getStencilOrDepthStencilAttachment() const
{
    return mState.getStencilOrDepthStencilAttachment();
}

const FramebufferAttachment *Framebuffer::getReadColorAttachment() const
{
    return mState.getReadAttachment();
}

GLenum Framebuffer::getReadColorAttachmentType() const
{
    const FramebufferAttachment *readAttachment = mState.getReadAttachment();
    return (readAttachment != nullptr ? readAttachment->type() : GL_NONE);
}

const FramebufferAttachment *Framebuffer::getFirstColorAttachment() const
{
    return mState.getFirstColorAttachment();
}

const FramebufferAttachment *Framebuffer::getFirstNonNullAttachment() const
{
    return mState.getFirstNonNullAttachment();
}

const FramebufferAttachment *Framebuffer::getAttachment(const Context *context,
                                                        GLenum attachment) const
{
    return mState.getAttachment(context, 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 DrawBuffersVector<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);

    mState.mEnabledDrawBuffers.reset();
    mState.mDrawBufferTypeMask.reset();

    for (size_t index = 0; index < count; ++index)
    {
        SetComponentTypeMask(getDrawbufferWriteType(index), index, &mState.mDrawBufferTypeMask);

        if (drawStates[index] != GL_NONE && mState.mColorAttachments[index].isAttached())
        {
            mState.mEnabledDrawBuffers.set(index);
        }
    }
}

const FramebufferAttachment *Framebuffer::getDrawBuffer(size_t drawBuffer) const
{
    return mState.getDrawBuffer(drawBuffer);
}

ComponentType Framebuffer::getDrawbufferWriteType(size_t drawBuffer) const
{
    const FramebufferAttachment *attachment = mState.getDrawBuffer(drawBuffer);
    if (attachment == nullptr)
    {
        return ComponentType::NoType;
    }

    GLenum componentType = attachment->getFormat().info->componentType;
    switch (componentType)
    {
        case GL_INT:
            return ComponentType::Int;
        case GL_UNSIGNED_INT:
            return ComponentType::UnsignedInt;

        default:
            return ComponentType::Float;
    }
}

ComponentTypeMask Framebuffer::getDrawBufferTypeMask() const
{
    return mState.mDrawBufferTypeMask;
}

DrawBufferMask Framebuffer::getDrawBufferMask() const
{
    return mState.mEnabledDrawBuffers;
}

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()));
    if (mState.mReadBufferState != buffer)
    {
        mState.mReadBufferState = buffer;
        mDirtyBits.set(DIRTY_BIT_READ_BUFFER);
    }
}

size_t Framebuffer::getNumColorAttachments() const
{
    return mState.mColorAttachments.size();
}

bool Framebuffer::hasDepth() const
{
    return mState.hasDepth();
}

bool Framebuffer::hasStencil() const
{
    return mState.hasStencil();
}

bool Framebuffer::hasExternalTextureAttachment() const
{
    return mState.hasExternalTextureAttachment();
}

bool Framebuffer::hasYUVAttachment() const
{
    return mState.hasYUVAttachment();
}

bool Framebuffer::usingExtendedDrawBuffers() const
{
    for (size_t drawbufferIdx = 1; drawbufferIdx < mState.mDrawBufferStates.size(); ++drawbufferIdx)
    {
        if (getDrawBuffer(drawbufferIdx) != nullptr)
        {
            return true;
        }
    }

    return false;
}

void Framebuffer::invalidateCompletenessCache()
{
    if (!isDefault())
    {
        mCachedStatus.reset();
    }
    onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
}

const FramebufferStatus &Framebuffer::checkStatusImpl(const Context *context) const
{
    ASSERT(!isDefault());
    ASSERT(hasAnyDirtyBit() || !mCachedStatus.valid());

    mCachedStatus = checkStatusWithGLFrontEnd(context);

    if (mCachedStatus.value().isComplete())
    {
        // We can skip syncState on several back-ends.
        if (mImpl->shouldSyncStateBeforeCheckStatus())
        {
            // This binding is not totally correct. It is ok because the parameter isn't used in
            // the GL back-end and the GL back-end is the only user of syncStateBeforeCheckStatus.
            angle::Result err = syncState(context, GL_FRAMEBUFFER, Command::Other);
            if (err != angle::Result::Continue)
            {
                mCachedStatus =
                    FramebufferStatus::Incomplete(0, err::kFramebufferIncompleteInternalError);
                return mCachedStatus.value();
            }
        }

        mCachedStatus = mImpl->checkStatus(context);
    }

    return mCachedStatus.value();
}

FramebufferStatus Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
{
    const State &state = context->getState();

    ASSERT(!isDefault());

    bool hasAttachments = false;
    Optional<unsigned int> colorbufferSize;
    Optional<int> samples;
    Optional<bool> fixedSampleLocations;
    bool hasRenderbuffer = false;
    Optional<int> renderToTextureSamples;

    const FramebufferAttachment *firstAttachment = getFirstNonNullAttachment();

    Optional<bool> isLayered;
    Optional<TextureType> colorAttachmentsTextureType;

    for (const FramebufferAttachment &colorAttachment : mState.mColorAttachments)
    {
        if (colorAttachment.isAttached())
        {
            FramebufferStatus attachmentCompleteness =
                CheckAttachmentCompleteness(context, colorAttachment);
            if (!attachmentCompleteness.isComplete())
            {
                return attachmentCompleteness;
            }

            const InternalFormat &format = *colorAttachment.getFormat().info;
            if (format.depthBits > 0 || format.stencilBits > 0)
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                    err::kFramebufferIncompleteDepthStencilInColorBuffer);
            }

            FramebufferStatus attachmentSampleCompleteness =
                CheckAttachmentSampleCompleteness(context, colorAttachment, true, &samples,
                                                  &fixedSampleLocations, &renderToTextureSamples);
            if (!attachmentSampleCompleteness.isComplete())
            {
                return attachmentSampleCompleteness;
            }

            // 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 (colorbufferSize.valid())
                {
                    if (format.pixelBytes != colorbufferSize.value())
                    {
                        return FramebufferStatus::Incomplete(
                            GL_FRAMEBUFFER_UNSUPPORTED,
                            err::kFramebufferIncompleteAttachmentInconsistantBitPlanes);
                    }
                }
                else
                {
                    colorbufferSize = format.pixelBytes;
                }
            }

            FramebufferStatus attachmentMultiviewCompleteness =
                CheckMultiviewStateMatchesForCompleteness(firstAttachment, &colorAttachment);
            if (!attachmentMultiviewCompleteness.isComplete())
            {
                return attachmentMultiviewCompleteness;
            }

            hasRenderbuffer = hasRenderbuffer || (colorAttachment.type() == GL_RENDERBUFFER);

            if (!hasAttachments)
            {
                isLayered = colorAttachment.isLayered();
                if (isLayered.value())
                {
                    colorAttachmentsTextureType = colorAttachment.getTextureImageIndex().getType();
                }
                hasAttachments = true;
            }
            else
            {
                // [EXT_geometry_shader] section 9.4.1, "Framebuffer Completeness"
                // If any framebuffer attachment is layered, all populated attachments
                // must be layered. Additionally, all populated color attachments must
                // be from textures of the same target. {FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT }
                ASSERT(isLayered.valid());
                if (isLayered.value() != colorAttachment.isLayered())
                {
                    return FramebufferStatus::Incomplete(
                        GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
                        err::kFramebufferIncompleteMismatchedLayeredAttachments);
                }
                else if (isLayered.value())
                {
                    ASSERT(colorAttachmentsTextureType.valid());
                    if (colorAttachmentsTextureType.value() !=
                        colorAttachment.getTextureImageIndex().getType())
                    {
                        return FramebufferStatus::Incomplete(
                            GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
                            err::kFramebufferIncompleteMismatchedLayeredTexturetypes);
                    }
                }
            }
        }
    }

    const FramebufferAttachment &depthAttachment = mState.mDepthAttachment;
    if (depthAttachment.isAttached())
    {
        FramebufferStatus attachmentCompleteness =
            CheckAttachmentCompleteness(context, depthAttachment);
        if (!attachmentCompleteness.isComplete())
        {
            return attachmentCompleteness;
        }

        const InternalFormat &format = *depthAttachment.getFormat().info;
        if (format.depthBits == 0)
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                err::kFramebufferIncompleteAttachmentNoDepthBitsInDepthBuffer);
        }

        FramebufferStatus attachmentSampleCompleteness =
            CheckAttachmentSampleCompleteness(context, depthAttachment, false, &samples,
                                              &fixedSampleLocations, &renderToTextureSamples);
        if (!attachmentSampleCompleteness.isComplete())
        {
            return attachmentSampleCompleteness;
        }

        FramebufferStatus attachmentMultiviewCompleteness =
            CheckMultiviewStateMatchesForCompleteness(firstAttachment, &depthAttachment);
        if (!attachmentMultiviewCompleteness.isComplete())
        {
            return attachmentMultiviewCompleteness;
        }

        hasRenderbuffer = hasRenderbuffer || (depthAttachment.type() == GL_RENDERBUFFER);

        if (!hasAttachments)
        {
            isLayered      = depthAttachment.isLayered();
            hasAttachments = true;
        }
        else
        {
            // [EXT_geometry_shader] section 9.4.1, "Framebuffer Completeness"
            // If any framebuffer attachment is layered, all populated attachments
            // must be layered. {FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT }
            ASSERT(isLayered.valid());
            if (isLayered.value() != depthAttachment.isLayered())
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
                    err::kFramebufferIncompleteMismatchedLayeredAttachments);
            }
        }
    }

    const FramebufferAttachment &stencilAttachment = mState.mStencilAttachment;
    if (stencilAttachment.isAttached())
    {
        FramebufferStatus attachmentCompleteness =
            CheckAttachmentCompleteness(context, stencilAttachment);
        if (!attachmentCompleteness.isComplete())
        {
            return attachmentCompleteness;
        }

        const InternalFormat &format = *stencilAttachment.getFormat().info;
        if (format.stencilBits == 0)
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                err::kFramebufferIncompleteAttachmentNoStencilBitsInStencilBuffer);
        }

        FramebufferStatus attachmentSampleCompleteness =
            CheckAttachmentSampleCompleteness(context, stencilAttachment, false, &samples,
                                              &fixedSampleLocations, &renderToTextureSamples);
        if (!attachmentSampleCompleteness.isComplete())
        {
            return attachmentSampleCompleteness;
        }

        FramebufferStatus attachmentMultiviewCompleteness =
            CheckMultiviewStateMatchesForCompleteness(firstAttachment, &stencilAttachment);
        if (!attachmentMultiviewCompleteness.isComplete())
        {
            return attachmentMultiviewCompleteness;
        }

        hasRenderbuffer = hasRenderbuffer || (stencilAttachment.type() == GL_RENDERBUFFER);

        if (!hasAttachments)
        {
            hasAttachments = true;
        }
        else
        {
            // [EXT_geometry_shader] section 9.4.1, "Framebuffer Completeness"
            // If any framebuffer attachment is layered, all populated attachments
            // must be layered.
            // {FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT }
            ASSERT(isLayered.valid());
            if (isLayered.value() != stencilAttachment.isLayered())
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
                    err::kFramebufferIncompleteMismatchedLayeredAttachments);
            }
        }
    }

    // Starting from ES 3.0 stencil and depth, if present, should be the same image
    if (state.getClientMajorVersion() >= 3 && depthAttachment.isAttached() &&
        stencilAttachment.isAttached() && stencilAttachment != depthAttachment)
    {
        return FramebufferStatus::Incomplete(
            GL_FRAMEBUFFER_UNSUPPORTED,
            err::kFramebufferIncompleteDepthAndStencilBuffersNotTheSame);
    }

    // Special additional validation for WebGL 1 DEPTH/STENCIL/DEPTH_STENCIL.
    if (state.isWebGL1())
    {
        if (!mState.mWebGLDepthStencilConsistent)
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_UNSUPPORTED,
                err::kFramebufferIncompleteWebGLDepthStencilInconsistant);
        }

        if (mState.mWebGLDepthStencilAttachment.isAttached())
        {
            if (mState.mWebGLDepthStencilAttachment.getDepthSize() == 0 ||
                mState.mWebGLDepthStencilAttachment.getStencilSize() == 0)
            {
                return FramebufferStatus::Incomplete(
                    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                    err::kFramebufferIncompleteAttachmentWebGLDepthStencilNoDepthOrStencilBits);
            }

            FramebufferStatus attachmentMultiviewCompleteness =
                CheckMultiviewStateMatchesForCompleteness(firstAttachment,
                                                          &mState.mWebGLDepthStencilAttachment);
            if (!attachmentMultiviewCompleteness.isComplete())
            {
                return attachmentMultiviewCompleteness;
            }
        }
        else if (mState.mStencilAttachment.isAttached() &&
                 mState.mStencilAttachment.getDepthSize() > 0)
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                err::kFramebufferIncompleteAttachmentWebGLStencilBufferHasDepthBits);
        }
        else if (mState.mDepthAttachment.isAttached() &&
                 mState.mDepthAttachment.getStencilSize() > 0)
        {
            return FramebufferStatus::Incomplete(
                GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
                err::kFramebufferIncompleteAttachmentWebGLDepthBufferHasStencilBits);
        }
    }

    // ES3.1(section 9.4) requires that if no image is attached to the framebuffer, and either the
    // value of the framebuffer's FRAMEBUFFER_DEFAULT_WIDTH or FRAMEBUFFER_DEFAULT_HEIGHT parameters
    // is zero, the framebuffer is considered incomplete.
    GLint defaultWidth  = mState.getDefaultWidth();
    GLint defaultHeight = mState.getDefaultHeight();
    if (!hasAttachments && (defaultWidth == 0 || defaultHeight == 0))
    {
        return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
                                             err::kFramebufferIncompleteDefaultZeroSize);
    }

    // In ES 2.0 and WebGL, all color attachments must have the same width and height.
    // In ES 3.0, there is no such restriction.
    if ((state.getClientMajorVersion() < 3 || state.getExtensions().webglCompatibilityANGLE) &&
        !mState.attachmentsHaveSameDimensions())
    {
        return FramebufferStatus::Incomplete(
            GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS,
            err::kFramebufferIncompleteInconsistantAttachmentSizes);
    }

    // ES3.1(section 9.4) requires that if the attached images are a mix of renderbuffers and
    // textures, the value of TEXTURE_FIXED_SAMPLE_LOCATIONS must be TRUE for all attached textures.
    if (fixedSampleLocations.valid() && hasRenderbuffer && !fixedSampleLocations.value())
    {
        return FramebufferStatus::Incomplete(
            GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
            err::kFramebufferIncompleteMultisampleNonFixedSamplesWithRenderbuffers);
    }

    // The WebGL conformance tests implicitly define that all framebuffer
    // attachments must be unique. For example, the same level of a texture can
    // not be attached to two different color attachments.
    if (state.getExtensions().webglCompatibilityANGLE)
    {
        if (!mState.colorAttachmentsAreUniqueImages())
        {
            return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNSUPPORTED,
                                                 err::kFramebufferIncompleteAttachmentsNotUnique);
        }
    }

    return FramebufferStatus::Complete();
}

angle::Result Framebuffer::discard(const Context *context, size_t count, const GLenum *attachments)
{
    // Back-ends might make the contents of the FBO undefined. In WebGL 2.0, invalidate operations
    // can be no-ops, so we should probably do that to ensure consistency.
    // TODO(jmadill): WebGL behaviour, and robust resource init behaviour without WebGL.

    return mImpl->discard(context, count, attachments);
}

angle::Result Framebuffer::invalidate(const Context *context,
                                      size_t count,
                                      const GLenum *attachments)
{
    // Back-ends might make the contents of the FBO undefined. In WebGL 2.0, invalidate operations
    // can be no-ops, so we should probably do that to ensure consistency.
    // TODO(jmadill): WebGL behaviour, and robust resource init behaviour without WebGL.

    return mImpl->invalidate(context, count, attachments);
}

bool Framebuffer::partialClearNeedsInit(const Context *context,
                                        bool color,
                                        bool depth,
                                        bool stencil)
{
    const auto &glState = context->getState();

    if (!glState.isRobustResourceInitEnabled())
    {
        return false;
    }

    if (depth && context->getFrontendFeatures().forceDepthAttachmentInitOnClear.enabled)
    {
        return true;
    }

    // Scissors can affect clearing.
    // TODO(jmadill): Check for complete scissor overlap.
    if (glState.isScissorTestEnabled())
    {
        return true;
    }

    // If colors masked, we must clear before we clear. Do a simple check.
    // TODO(jmadill): Filter out unused color channels from the test.
    if (color && glState.anyActiveDrawBufferChannelMasked())
    {
        return true;
    }

    const auto &depthStencil = glState.getDepthStencilState();
    if (stencil && (depthStencil.stencilMask != depthStencil.stencilWritemask ||
                    depthStencil.stencilBackMask != depthStencil.stencilBackWritemask))
    {
        return true;
    }

    return false;
}

angle::Result Framebuffer::invalidateSub(const Context *context,
                                         size_t count,
                                         const GLenum *attachments,
                                         const Rectangle &area)
{
    // Back-ends might make the contents of the FBO undefined. In WebGL 2.0, invalidate operations
    // can be no-ops, so we should probably do that to ensure consistency.
    // TODO(jmadill): Make a invalidate no-op in WebGL 2.0.

    return mImpl->invalidateSub(context, count, attachments, area);
}

angle::Result Framebuffer::clear(const Context *context, GLbitfield mask)
{
    ASSERT(mask && !context->getState().isRasterizerDiscardEnabled());

    return mImpl->clear(context, mask);
}

angle::Result Framebuffer::clearBufferfv(const Context *context,
                                         GLenum buffer,
                                         GLint drawbuffer,
                                         const GLfloat *values)
{
    return mImpl->clearBufferfv(context, buffer, drawbuffer, values);
}

angle::Result Framebuffer::clearBufferuiv(const Context *context,
                                          GLenum buffer,
                                          GLint drawbuffer,
                                          const GLuint *values)
{
    return mImpl->clearBufferuiv(context, buffer, drawbuffer, values);
}

angle::Result Framebuffer::clearBufferiv(const Context *context,
                                         GLenum buffer,
                                         GLint drawbuffer,
                                         const GLint *values)
{
    return mImpl->clearBufferiv(context, buffer, drawbuffer, values);
}

angle::Result Framebuffer::clearBufferfi(const Context *context,
                                         GLenum buffer,
                                         GLint drawbuffer,
                                         GLfloat depth,
                                         GLint stencil)
{
    const bool clearDepth =
        getDepthAttachment() != nullptr && context->getState().getDepthStencilState().depthMask;
    const bool clearStencil = getStencilAttachment() != nullptr &&
                              context->getState().getDepthStencilState().stencilWritemask != 0;

    if (clearDepth && clearStencil)
    {
        ASSERT(buffer == GL_DEPTH_STENCIL);
        ANGLE_TRY(mImpl->clearBufferfi(context, GL_DEPTH_STENCIL, drawbuffer, depth, stencil));
    }
    else if (clearDepth && !clearStencil)
    {
        ANGLE_TRY(mImpl->clearBufferfv(context, GL_DEPTH, drawbuffer, &depth));
    }
    else if (!clearDepth && clearStencil)
    {
        ANGLE_TRY(mImpl->clearBufferiv(context, GL_STENCIL, drawbuffer, &stencil));
    }

    return angle::Result::Continue;
}

GLenum Framebuffer::getImplementationColorReadFormat(const Context *context)
{
    const gl::InternalFormat &format = mImpl->getImplementationColorReadFormat(context);
    return format.getReadPixelsFormat(context->getExtensions());
}

GLenum Framebuffer::getImplementationColorReadType(const Context *context)
{
    const gl::InternalFormat &format = mImpl->getImplementationColorReadFormat(context);
    return format.getReadPixelsType(context->getClientVersion());
}

angle::Result Framebuffer::readPixels(const Context *context,
                                      const Rectangle &area,
                                      GLenum format,
                                      GLenum type,
                                      const PixelPackState &pack,
                                      Buffer *packBuffer,
                                      void *pixels)
{
    ANGLE_TRY(mImpl->readPixels(context, area, format, type, pack, packBuffer, pixels));

    if (packBuffer)
    {
        packBuffer->onDataChanged();
    }

    return angle::Result::Continue;
}

angle::Result Framebuffer::blit(const Context *context,
                                const Rectangle &sourceArea,
                                const Rectangle &destArea,
                                GLbitfield mask,
                                GLenum filter)
{
    ASSERT(mask != 0);

    ANGLE_TRY(mImpl->blit(context, sourceArea, destArea, mask, filter));

    // Mark the contents of the attachments dirty
    if ((mask & GL_COLOR_BUFFER_BIT) != 0)
    {
        for (size_t colorIndex : mState.mEnabledDrawBuffers)
        {
            mDirtyBits.set(DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + colorIndex);
        }
    }
    if ((mask & GL_DEPTH_BUFFER_BIT) != 0)
    {
        mDirtyBits.set(DIRTY_BIT_DEPTH_BUFFER_CONTENTS);
    }
    if ((mask & GL_STENCIL_BUFFER_BIT) != 0)
    {
        mDirtyBits.set(DIRTY_BIT_STENCIL_BUFFER_CONTENTS);
    }
    onStateChange(angle::SubjectMessage::DirtyBitsFlagged);

    return angle::Result::Continue;
}

int Framebuffer::getSamples(const Context *context) const
{
    if (!isComplete(context))
    {
        return 0;
    }

    ASSERT(mCachedStatus.valid() && mCachedStatus.value().isComplete());

    // For a complete framebuffer, all attachments must have the same sample count.
    // In this case return the first nonzero sample size.
    const FramebufferAttachment *firstNonNullAttachment = mState.getFirstNonNullAttachment();
    ASSERT(firstNonNullAttachment == nullptr || firstNonNullAttachment->isAttached());

    return firstNonNullAttachment ? firstNonNullAttachment->getSamples() : 0;
}

int Framebuffer::getReadBufferResourceSamples(const Context *context) const
{
    if (!isComplete(context))
    {
        return 0;
    }

    ASSERT(mCachedStatus.valid() && mCachedStatus.value().isComplete());

    const FramebufferAttachment *readAttachment = mState.getReadAttachment();
    ASSERT(readAttachment == nullptr || readAttachment->isAttached());

    return readAttachment ? readAttachment->getResourceSamples() : 0;
}

angle::Result Framebuffer::getSamplePosition(const Context *context,
                                             size_t index,
                                             GLfloat *xy) const
{
    ANGLE_TRY(mImpl->getSamplePosition(context, index, xy));
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=98 G=95

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge