summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/Framebuffer.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/angle/checkout/src/libANGLE/Framebuffer.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/Framebuffer.cpp')
-rw-r--r--gfx/angle/checkout/src/libANGLE/Framebuffer.cpp2383
1 files changed, 2383 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/Framebuffer.cpp b/gfx/angle/checkout/src/libANGLE/Framebuffer.cpp
new file mode 100644
index 0000000000..64acc8a4cd
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/Framebuffer.cpp
@@ -0,0 +1,2383 @@
+//
+// 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/bitset_utils.h"
+#include "common/utilities.h"
+#include "libANGLE/Config.h"
+#include "libANGLE/Context.h"
+#include "libANGLE/Display.h"
+#include "libANGLE/FramebufferAttachment.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
+{
+
+bool CheckMultiviewStateMatchesForCompleteness(const FramebufferAttachment *firstAttachment,
+ const FramebufferAttachment *secondAttachment)
+{
+ ASSERT(firstAttachment && secondAttachment);
+ ASSERT(firstAttachment->isAttached() && secondAttachment->isAttached());
+
+ if (firstAttachment->getNumViews() != secondAttachment->getNumViews())
+ {
+ return false;
+ }
+ if (firstAttachment->getBaseViewIndex() != secondAttachment->getBaseViewIndex())
+ {
+ return false;
+ }
+ if (firstAttachment->isMultiview() != secondAttachment->isMultiview())
+ {
+ return false;
+ }
+ return true;
+}
+
+bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttachment &attachment)
+{
+ ASSERT(attachment.isAttached());
+
+ const Extents &size = attachment.getSize();
+ if (size.width == 0 || size.height == 0)
+ {
+ return false;
+ }
+
+ if (!attachment.isRenderable(context))
+ {
+ return false;
+ }
+
+ 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 false;
+ }
+ }
+ // 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 (static_cast<GLuint>(size.depth) >= context->getCaps().maxFramebufferLayers)
+ {
+ return false;
+ }
+ }
+
+ // 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 false;
+ }
+
+ 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 false;
+ }
+
+ // 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 false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CheckAttachmentSampleCompleteness(const Context *context,
+ const FramebufferAttachment &attachment,
+ bool colorAttachment,
+ Optional<int> *samples,
+ Optional<bool> *fixedSampleLocations)
+{
+ ASSERT(attachment.isAttached());
+
+ if (attachment.type() == GL_TEXTURE)
+ {
+ const Texture *texture = attachment.getTexture();
+ ASSERT(texture);
+
+ const ImageIndex &attachmentImageIndex = attachment.getTextureImageIndex();
+ bool fixedSampleloc = texture->getAttachmentFixedSampleLocations(attachmentImageIndex);
+ if (fixedSampleLocations->valid() && fixedSampleloc != fixedSampleLocations->value())
+ {
+ return false;
+ }
+ else
+ {
+ *fixedSampleLocations = fixedSampleloc;
+ }
+ }
+
+ if (samples->valid())
+ {
+ if (attachment.getSamples() != samples->value())
+ {
+ 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 false;
+ }
+ 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().framebufferMixedSamples)
+ {
+ return false;
+ }
+
+ if ((attachment.getSamples() % std::max(samples->value(), 1)) != 0)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ *samples = attachment.getSamples();
+ }
+
+ return true;
+}
+
+// Needed to index into the attachment arrays/bitsets.
+static_assert(static_cast<size_t>(IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS) ==
+ Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX,
+ "Framebuffer Dirty bit mismatch");
+static_assert(static_cast<size_t>(IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS) ==
+ Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT,
+ "Framebuffer Dirty bit mismatch");
+static_assert(static_cast<size_t>(IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 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 IsColorMaskedOut(const BlendState &blend)
+{
+ return (!blend.colorMaskRed && !blend.colorMaskGreen && !blend.colorMaskBlue &&
+ !blend.colorMaskAlpha);
+}
+
+bool IsDepthMaskedOut(const DepthStencilState &depthStencil)
+{
+ return !depthStencil.depthMask;
+}
+
+bool IsStencilMaskedOut(const DepthStencilState &depthStencil)
+{
+ return ((depthStencil.stencilMask & depthStencil.stencilWritemask) == 0);
+}
+
+bool IsClearBufferMaskedOut(const Context *context, GLenum buffer)
+{
+ switch (buffer)
+ {
+ case GL_COLOR:
+ return IsColorMaskedOut(context->getState().getBlendState());
+ case GL_DEPTH:
+ return IsDepthMaskedOut(context->getState().getDepthStencilState());
+ case GL_STENCIL:
+ return IsStencilMaskedOut(context->getState().getDepthStencilState());
+ case GL_DEPTH_STENCIL:
+ return IsDepthMaskedOut(context->getState().getDepthStencilState()) &&
+ IsStencilMaskedOut(context->getState().getDepthStencilState());
+ default:
+ UNREACHABLE();
+ return true;
+ }
+}
+
+} // anonymous namespace
+
+// This constructor is only used for default framebuffers.
+FramebufferState::FramebufferState()
+ : mId(0),
+ mLabel(),
+ mColorAttachments(1),
+ mDrawBufferStates(1, GL_BACK),
+ mReadBufferState(GL_BACK),
+ mDrawBufferTypeMask(),
+ mDefaultWidth(0),
+ mDefaultHeight(0),
+ mDefaultSamples(0),
+ mDefaultFixedSampleLocations(GL_FALSE),
+ mDefaultLayers(0),
+ mWebGLDepthStencilConsistent(true)
+{
+ ASSERT(mDrawBufferStates.size() > 0);
+ mEnabledDrawBuffers.set(0);
+}
+
+FramebufferState::FramebufferState(const Caps &caps, GLuint id)
+ : mId(id),
+ mLabel(),
+ mColorAttachments(caps.maxColorAttachments),
+ mDrawBufferStates(caps.maxDrawBuffers, GL_NONE),
+ mReadBufferState(GL_COLOR_ATTACHMENT0_EXT),
+ mDrawBufferTypeMask(),
+ mDefaultWidth(0),
+ mDefaultHeight(0),
+ mDefaultSamples(0),
+ mDefaultFixedSampleLocations(GL_FALSE),
+ mDefaultLayers(0),
+ mWebGLDepthStencilConsistent(true)
+{
+ ASSERT(mId != 0);
+ ASSERT(mDrawBufferStates.size() > 0);
+ mDrawBufferStates[0] = GL_COLOR_ATTACHMENT0_EXT;
+}
+
+FramebufferState::~FramebufferState() {}
+
+const std::string &FramebufferState::getLabel()
+{
+ 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;
+ }
+}
+
+size_t FramebufferState::getReadIndex() const
+{
+ 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 readIndex;
+}
+
+const FramebufferAttachment *FramebufferState::getReadAttachment() const
+{
+ if (mReadBufferState == GL_NONE)
+ {
+ return nullptr;
+ }
+ size_t readIndex = getReadIndex();
+ return mColorAttachments[readIndex].isAttached() ? &mColorAttachments[readIndex] : nullptr;
+}
+
+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;
+}
+
+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::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
+{
+ ASSERT(attachmentsHaveSameDimensions());
+ ASSERT(getFirstNonNullAttachment() != nullptr);
+ return getFirstNonNullAttachment()->getSize();
+}
+
+Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, GLuint id)
+ : mState(caps, id),
+ 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>(caps.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);
+ }
+}
+
+Framebuffer::Framebuffer(const Context *context, egl::Surface *surface)
+ : mState(),
+ mImpl(surface->getImplementation()->createDefaultFramebuffer(context, mState)),
+ mCachedStatus(GL_FRAMEBUFFER_COMPLETE),
+ mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
+ mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
+{
+ ASSERT(mImpl != nullptr);
+ mDirtyColorAttachmentBindings.emplace_back(this, DIRTY_BIT_COLOR_ATTACHMENT_0);
+
+ setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_BACK, ImageIndex(), surface,
+ FramebufferAttachment::kDefaultNumViews,
+ FramebufferAttachment::kDefaultBaseViewIndex, false);
+
+ if (surface->getConfig()->depthSize > 0)
+ {
+ setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_DEPTH, ImageIndex(), surface,
+ FramebufferAttachment::kDefaultNumViews,
+ FramebufferAttachment::kDefaultBaseViewIndex, false);
+ }
+
+ if (surface->getConfig()->stencilSize > 0)
+ {
+ setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_STENCIL, ImageIndex(), surface,
+ FramebufferAttachment::kDefaultNumViews,
+ FramebufferAttachment::kDefaultBaseViewIndex, false);
+ }
+ SetComponentTypeMask(getDrawbufferWriteType(0), 0, &mState.mDrawBufferTypeMask);
+
+ // Ensure the backend has a chance to synchronize its content for a new backbuffer.
+ mDirtyBits.set(DIRTY_BIT_COLOR_BUFFER_CONTENTS_0);
+}
+
+Framebuffer::Framebuffer(rx::GLImplFactory *factory)
+ : mState(),
+ mImpl(factory->createFramebuffer(mState)),
+ mCachedStatus(GL_FRAMEBUFFER_UNDEFINED_OES),
+ 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()
+{
+ SafeDelete(mImpl);
+}
+
+void Framebuffer::onDestroy(const Context *context)
+{
+ for (auto &attachment : mState.mColorAttachments)
+ {
+ attachment.detach(context);
+ }
+ mState.mDepthAttachment.detach(context);
+ mState.mStencilAttachment.detach(context);
+ mState.mWebGLDepthAttachment.detach(context);
+ mState.mWebGLStencilAttachment.detach(context);
+ mState.mWebGLDepthStencilAttachment.detach(context);
+
+ mImpl->destroy(context);
+}
+
+void Framebuffer::setLabel(const Context *context, const std::string &label)
+{
+ mState.mLabel = label;
+}
+
+const std::string &Framebuffer::getLabel() const
+{
+ return mState.mLabel;
+}
+
+bool Framebuffer::detachTexture(const Context *context, GLuint textureId)
+{
+ return detachResourceById(context, GL_TEXTURE, textureId);
+}
+
+bool Framebuffer::detachRenderbuffer(const Context *context, GLuint renderbufferId)
+{
+ return detachResourceById(context, GL_RENDERBUFFER, renderbufferId);
+}
+
+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 colorAttachment) const
+{
+ 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 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);
+
+ 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()));
+ 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::usingExtendedDrawBuffers() const
+{
+ for (size_t drawbufferIdx = 1; drawbufferIdx < mState.mDrawBufferStates.size(); ++drawbufferIdx)
+ {
+ if (getDrawBuffer(drawbufferIdx) != nullptr)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Framebuffer::invalidateCompletenessCache()
+{
+ if (mState.mId != 0)
+ {
+ mCachedStatus.reset();
+ }
+ onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
+}
+
+GLenum Framebuffer::checkStatusImpl(const Context *context)
+{
+ ASSERT(!isDefault());
+ ASSERT(hasAnyDirtyBit() || !mCachedStatus.valid());
+
+ mCachedStatus = checkStatusWithGLFrontEnd(context);
+
+ if (mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE)
+ {
+ // We can skip syncState on several back-ends.
+ if (mImpl->shouldSyncStateBeforeCheckStatus())
+ {
+ angle::Result err = syncState(context);
+ if (err != angle::Result::Continue)
+ {
+ return 0;
+ }
+ }
+
+ if (!mImpl->checkStatus(context))
+ {
+ mCachedStatus = GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+ }
+
+ return mCachedStatus.value();
+}
+
+GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context)
+{
+ const State &state = context->getState();
+
+ ASSERT(mState.mId != 0);
+
+ bool hasAttachments = false;
+ Optional<unsigned int> colorbufferSize;
+ Optional<int> samples;
+ Optional<bool> fixedSampleLocations;
+ bool hasRenderbuffer = false;
+
+ const FramebufferAttachment *firstAttachment = getFirstNonNullAttachment();
+
+ Optional<bool> isLayered;
+ Optional<TextureType> colorAttachmentsTextureType;
+
+ for (const FramebufferAttachment &colorAttachment : mState.mColorAttachments)
+ {
+ if (colorAttachment.isAttached())
+ {
+ if (!CheckAttachmentCompleteness(context, colorAttachment))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+
+ const InternalFormat &format = *colorAttachment.getFormat().info;
+ if (format.depthBits > 0 || format.stencilBits > 0)
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+
+ if (!CheckAttachmentSampleCompleteness(context, colorAttachment, true, &samples,
+ &fixedSampleLocations))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
+ }
+
+ // 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 GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+ }
+ else
+ {
+ colorbufferSize = format.pixelBytes;
+ }
+ }
+
+ if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment, &colorAttachment))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
+ }
+
+ 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 GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT;
+ }
+ else if (isLayered.value())
+ {
+ ASSERT(colorAttachmentsTextureType.valid());
+ if (colorAttachmentsTextureType.value() !=
+ colorAttachment.getTextureImageIndex().getType())
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT;
+ }
+ }
+ }
+ }
+ }
+
+ const FramebufferAttachment &depthAttachment = mState.mDepthAttachment;
+ if (depthAttachment.isAttached())
+ {
+ if (!CheckAttachmentCompleteness(context, depthAttachment))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+
+ const InternalFormat &format = *depthAttachment.getFormat().info;
+ if (format.depthBits == 0)
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+
+ if (!CheckAttachmentSampleCompleteness(context, depthAttachment, false, &samples,
+ &fixedSampleLocations))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
+ }
+
+ if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment, &depthAttachment))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
+ }
+
+ 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 GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT;
+ }
+ }
+ }
+
+ const FramebufferAttachment &stencilAttachment = mState.mStencilAttachment;
+ if (stencilAttachment.isAttached())
+ {
+ if (!CheckAttachmentCompleteness(context, stencilAttachment))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+
+ const InternalFormat &format = *stencilAttachment.getFormat().info;
+ if (format.stencilBits == 0)
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+
+ if (!CheckAttachmentSampleCompleteness(context, stencilAttachment, false, &samples,
+ &fixedSampleLocations))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
+ }
+
+ if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment, &stencilAttachment))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
+ }
+
+ 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 GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT;
+ }
+ }
+ }
+
+ // 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 GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+
+ // Special additional validation for WebGL 1 DEPTH/STENCIL/DEPTH_STENCIL.
+ if (state.isWebGL1())
+ {
+ if (!mState.mWebGLDepthStencilConsistent)
+ {
+ return GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+
+ if (mState.mWebGLDepthStencilAttachment.isAttached())
+ {
+ if (mState.mWebGLDepthStencilAttachment.getDepthSize() == 0 ||
+ mState.mWebGLDepthStencilAttachment.getStencilSize() == 0)
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+
+ if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment,
+ &mState.mWebGLDepthStencilAttachment))
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
+ }
+ }
+ else if (mState.mStencilAttachment.isAttached() &&
+ mState.mStencilAttachment.getDepthSize() > 0)
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+ else if (mState.mDepthAttachment.isAttached() &&
+ mState.mDepthAttachment.getStencilSize() > 0)
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+ }
+
+ // 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 GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+ }
+
+ // 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().webglCompatibility) &&
+ !mState.attachmentsHaveSameDimensions())
+ {
+ return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
+ }
+
+ // 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 GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
+ }
+
+ // 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().webglCompatibility)
+ {
+ if (!mState.colorAttachmentsAreUniqueImages())
+ {
+ return GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+ }
+
+ return GL_FRAMEBUFFER_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;
+ }
+
+ // 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)
+ {
+ const auto &blend = glState.getBlendState();
+ if (!(blend.colorMaskRed && blend.colorMaskGreen && blend.colorMaskBlue &&
+ blend.colorMaskAlpha))
+ {
+ 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)
+{
+ const auto &glState = context->getState();
+ if (glState.isRasterizerDiscardEnabled())
+ {
+ return angle::Result::Continue;
+ }
+
+ // Remove clear bits that are ineffective. An effective clear changes at least one fragment. If
+ // color/depth/stencil masks make the clear ineffective we skip it altogether.
+
+ // If all color channels are masked, don't attempt to clear color.
+ if (context->getState().getBlendState().allChannelsMasked())
+ {
+ mask &= ~GL_COLOR_BUFFER_BIT;
+ }
+
+ // If depth write is disabled, don't attempt to clear depth.
+ if (!context->getState().getDepthStencilState().depthMask)
+ {
+ mask &= ~GL_DEPTH_BUFFER_BIT;
+ }
+
+ // If all stencil bits are masked, don't attempt to clear stencil.
+ if (context->getState().getDepthStencilState().stencilWritemask == 0)
+ {
+ mask &= ~GL_STENCIL_BUFFER_BIT;
+ }
+
+ if (mask != 0)
+ {
+ ANGLE_TRY(mImpl->clear(context, mask));
+ }
+
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::clearBufferfv(const Context *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLfloat *values)
+{
+ if (context->getState().isRasterizerDiscardEnabled() || IsClearBufferMaskedOut(context, buffer))
+ {
+ return angle::Result::Continue;
+ }
+
+ if (buffer == GL_DEPTH)
+ {
+ // If depth write is disabled, don't attempt to clear depth.
+ if (!context->getState().getDepthStencilState().depthMask)
+ {
+ return angle::Result::Continue;
+ }
+ }
+ else
+ {
+ // If all color channels are masked, don't attempt to clear color.
+ if (context->getState().getBlendState().allChannelsMasked())
+ {
+ return angle::Result::Continue;
+ }
+ }
+
+ ANGLE_TRY(mImpl->clearBufferfv(context, buffer, drawbuffer, values));
+
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::clearBufferuiv(const Context *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLuint *values)
+{
+ if (context->getState().isRasterizerDiscardEnabled() || IsClearBufferMaskedOut(context, buffer))
+ {
+ return angle::Result::Continue;
+ }
+
+ // If all color channels are masked, don't attempt to clear color.
+ if (context->getState().getBlendState().allChannelsMasked())
+ {
+ return angle::Result::Continue;
+ }
+
+ ANGLE_TRY(mImpl->clearBufferuiv(context, buffer, drawbuffer, values));
+
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::clearBufferiv(const Context *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ const GLint *values)
+{
+ if (context->getState().isRasterizerDiscardEnabled() || IsClearBufferMaskedOut(context, buffer))
+ {
+ return angle::Result::Continue;
+ }
+
+ if (buffer == GL_STENCIL)
+ {
+ // If all stencil bits are masked, don't attempt to clear stencil.
+ if (context->getState().getDepthStencilState().stencilWritemask == 0)
+ {
+ return angle::Result::Continue;
+ }
+ }
+ else
+ {
+ // If all color channels are masked, don't attempt to clear color.
+ if (context->getState().getBlendState().allChannelsMasked())
+ {
+ return angle::Result::Continue;
+ }
+ }
+
+ ANGLE_TRY(mImpl->clearBufferiv(context, buffer, drawbuffer, values));
+
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::clearBufferfi(const Context *context,
+ GLenum buffer,
+ GLint drawbuffer,
+ GLfloat depth,
+ GLint stencil)
+{
+ if (context->getState().isRasterizerDiscardEnabled() || IsClearBufferMaskedOut(context, buffer))
+ {
+ return angle::Result::Continue;
+ }
+
+ bool clearDepth = context->getState().getDepthStencilState().depthMask;
+ bool clearStencil = 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;
+}
+
+angle::Result Framebuffer::getImplementationColorReadFormat(const Context *context,
+ GLenum *formatOut)
+{
+ ANGLE_TRY(syncState(context));
+ *formatOut = mImpl->getImplementationColorReadFormat(context);
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::getImplementationColorReadType(const Context *context, GLenum *typeOut)
+{
+ ANGLE_TRY(syncState(context));
+ *typeOut = mImpl->getImplementationColorReadType(context);
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::readPixels(const Context *context,
+ const Rectangle &area,
+ GLenum format,
+ GLenum type,
+ void *pixels)
+{
+ ANGLE_TRY(mImpl->readPixels(context, area, format, type, pixels));
+
+ Buffer *unpackBuffer = context->getState().getTargetBuffer(BufferBinding::PixelUnpack);
+ if (unpackBuffer)
+ {
+ unpackBuffer->onPixelPack();
+ }
+
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::blit(const Context *context,
+ const Rectangle &sourceArea,
+ const Rectangle &destArea,
+ GLbitfield mask,
+ GLenum filter)
+{
+ GLbitfield blitMask = mask;
+
+ // Note that blitting is called against draw framebuffer.
+ // See the code in gl::Context::blitFramebuffer.
+ if ((mask & GL_COLOR_BUFFER_BIT) && !hasEnabledDrawBuffer())
+ {
+ blitMask &= ~GL_COLOR_BUFFER_BIT;
+ }
+
+ if ((mask & GL_STENCIL_BUFFER_BIT) && mState.getStencilAttachment() == nullptr)
+ {
+ blitMask &= ~GL_STENCIL_BUFFER_BIT;
+ }
+
+ if ((mask & GL_DEPTH_BUFFER_BIT) && mState.getDepthAttachment() == nullptr)
+ {
+ blitMask &= ~GL_DEPTH_BUFFER_BIT;
+ }
+
+ if (!blitMask)
+ {
+ return angle::Result::Continue;
+ }
+
+ return mImpl->blit(context, sourceArea, destArea, blitMask, filter);
+}
+
+bool Framebuffer::isDefault() const
+{
+ return id() == 0;
+}
+
+int Framebuffer::getSamples(const Context *context)
+{
+ return (isComplete(context) ? getCachedSamples(context) : 0);
+}
+
+int Framebuffer::getCachedSamples(const Context *context) const
+{
+ ASSERT(mCachedStatus.valid() && mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE);
+
+ // For a complete framebuffer, all attachments must have the same sample count.
+ // In this case return the first nonzero sample size.
+ const auto *firstNonNullAttachment = mState.getFirstNonNullAttachment();
+ if (firstNonNullAttachment)
+ {
+ ASSERT(firstNonNullAttachment->isAttached());
+ return firstNonNullAttachment->getSamples();
+ }
+
+ // No attachments found.
+ return 0;
+}
+
+angle::Result Framebuffer::getSamplePosition(const Context *context,
+ size_t index,
+ GLfloat *xy) const
+{
+ ANGLE_TRY(mImpl->getSamplePosition(context, index, xy));
+ return angle::Result::Continue;
+}
+
+bool Framebuffer::hasValidDepthStencil() const
+{
+ return mState.getDepthStencilAttachment() != nullptr;
+}
+
+void Framebuffer::setAttachment(const Context *context,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource)
+{
+ setAttachment(context, type, binding, textureIndex, resource,
+ FramebufferAttachment::kDefaultNumViews,
+ FramebufferAttachment::kDefaultBaseViewIndex, false);
+}
+
+void Framebuffer::setAttachment(const Context *context,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource,
+ GLsizei numViews,
+ GLuint baseViewIndex,
+ bool isMultiview)
+{
+ // Context may be null in unit tests.
+ if (!context || !context->isWebGL1())
+ {
+ setAttachmentImpl(context, type, binding, textureIndex, resource, numViews, baseViewIndex,
+ isMultiview);
+ return;
+ }
+
+ switch (binding)
+ {
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH_STENCIL_ATTACHMENT:
+ mState.mWebGLDepthStencilAttachment.attach(context, type, binding, textureIndex,
+ resource, numViews, baseViewIndex,
+ isMultiview);
+ break;
+ case GL_DEPTH:
+ case GL_DEPTH_ATTACHMENT:
+ mState.mWebGLDepthAttachment.attach(context, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview);
+ break;
+ case GL_STENCIL:
+ case GL_STENCIL_ATTACHMENT:
+ mState.mWebGLStencilAttachment.attach(context, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview);
+ break;
+ default:
+ setAttachmentImpl(context, type, binding, textureIndex, resource, numViews,
+ baseViewIndex, isMultiview);
+ return;
+ }
+
+ commitWebGL1DepthStencilIfConsistent(context, numViews, baseViewIndex, isMultiview);
+}
+
+void Framebuffer::setAttachmentMultiview(const Context *context,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource,
+ GLsizei numViews,
+ GLint baseViewIndex)
+{
+ setAttachment(context, type, binding, textureIndex, resource, numViews, baseViewIndex, true);
+}
+
+void Framebuffer::commitWebGL1DepthStencilIfConsistent(const Context *context,
+ GLsizei numViews,
+ GLuint baseViewIndex,
+ bool isMultiview)
+{
+ int count = 0;
+
+ std::array<FramebufferAttachment *, 3> attachments = {{&mState.mWebGLDepthStencilAttachment,
+ &mState.mWebGLDepthAttachment,
+ &mState.mWebGLStencilAttachment}};
+ for (FramebufferAttachment *attachment : attachments)
+ {
+ if (attachment->isAttached())
+ {
+ count++;
+ }
+ }
+
+ mState.mWebGLDepthStencilConsistent = (count <= 1);
+ if (!mState.mWebGLDepthStencilConsistent)
+ {
+ // Inconsistent.
+ return;
+ }
+
+ auto getImageIndexIfTextureAttachment = [](const FramebufferAttachment &attachment) {
+ if (attachment.type() == GL_TEXTURE)
+ {
+ return attachment.getTextureImageIndex();
+ }
+ else
+ {
+ return ImageIndex();
+ }
+ };
+
+ if (mState.mWebGLDepthAttachment.isAttached())
+ {
+ const auto &depth = mState.mWebGLDepthAttachment;
+ setAttachmentImpl(context, depth.type(), GL_DEPTH_ATTACHMENT,
+ getImageIndexIfTextureAttachment(depth), depth.getResource(), numViews,
+ baseViewIndex, isMultiview);
+ setAttachmentImpl(context, GL_NONE, GL_STENCIL_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview);
+ }
+ else if (mState.mWebGLStencilAttachment.isAttached())
+ {
+ const auto &stencil = mState.mWebGLStencilAttachment;
+ setAttachmentImpl(context, GL_NONE, GL_DEPTH_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview);
+ setAttachmentImpl(context, stencil.type(), GL_STENCIL_ATTACHMENT,
+ getImageIndexIfTextureAttachment(stencil), stencil.getResource(),
+ numViews, baseViewIndex, isMultiview);
+ }
+ else if (mState.mWebGLDepthStencilAttachment.isAttached())
+ {
+ const auto &depthStencil = mState.mWebGLDepthStencilAttachment;
+ setAttachmentImpl(context, depthStencil.type(), GL_DEPTH_ATTACHMENT,
+ getImageIndexIfTextureAttachment(depthStencil),
+ depthStencil.getResource(), numViews, baseViewIndex, isMultiview);
+ setAttachmentImpl(context, depthStencil.type(), GL_STENCIL_ATTACHMENT,
+ getImageIndexIfTextureAttachment(depthStencil),
+ depthStencil.getResource(), numViews, baseViewIndex, isMultiview);
+ }
+ else
+ {
+ setAttachmentImpl(context, GL_NONE, GL_DEPTH_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview);
+ setAttachmentImpl(context, GL_NONE, GL_STENCIL_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview);
+ }
+}
+
+void Framebuffer::setAttachmentImpl(const Context *context,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource,
+ GLsizei numViews,
+ GLuint baseViewIndex,
+ bool isMultiview)
+{
+ switch (binding)
+ {
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH_STENCIL_ATTACHMENT:
+ {
+ // ensure this is a legitimate depth+stencil format
+ FramebufferAttachmentObject *attachmentObj = resource;
+ if (resource)
+ {
+ const Format &format = resource->getAttachmentFormat(binding, textureIndex);
+ if (format.info->depthBits == 0 || format.info->stencilBits == 0)
+ {
+ // Attaching nullptr detaches the current attachment.
+ attachmentObj = nullptr;
+ }
+ }
+
+ updateAttachment(context, &mState.mDepthAttachment, DIRTY_BIT_DEPTH_ATTACHMENT,
+ &mDirtyDepthAttachmentBinding, type, binding, textureIndex,
+ attachmentObj, numViews, baseViewIndex, isMultiview);
+ updateAttachment(context, &mState.mStencilAttachment, DIRTY_BIT_STENCIL_ATTACHMENT,
+ &mDirtyStencilAttachmentBinding, type, binding, textureIndex,
+ attachmentObj, numViews, baseViewIndex, isMultiview);
+ break;
+ }
+
+ case GL_DEPTH:
+ case GL_DEPTH_ATTACHMENT:
+ updateAttachment(context, &mState.mDepthAttachment, DIRTY_BIT_DEPTH_ATTACHMENT,
+ &mDirtyDepthAttachmentBinding, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview);
+ break;
+
+ case GL_STENCIL:
+ case GL_STENCIL_ATTACHMENT:
+ updateAttachment(context, &mState.mStencilAttachment, DIRTY_BIT_STENCIL_ATTACHMENT,
+ &mDirtyStencilAttachmentBinding, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview);
+ break;
+
+ case GL_BACK:
+ updateAttachment(context, &mState.mColorAttachments[0], DIRTY_BIT_COLOR_ATTACHMENT_0,
+ &mDirtyColorAttachmentBindings[0], type, binding, textureIndex,
+ resource, numViews, baseViewIndex, isMultiview);
+ break;
+
+ default:
+ {
+ size_t colorIndex = binding - GL_COLOR_ATTACHMENT0;
+ ASSERT(colorIndex < mState.mColorAttachments.size());
+ size_t dirtyBit = DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex;
+ updateAttachment(context, &mState.mColorAttachments[colorIndex], dirtyBit,
+ &mDirtyColorAttachmentBindings[colorIndex], type, binding,
+ textureIndex, resource, numViews, baseViewIndex, isMultiview);
+
+ if (!resource)
+ {
+ mColorAttachmentBits.reset(colorIndex);
+ mFloat32ColorAttachmentBits.reset(colorIndex);
+ }
+ else
+ {
+ mColorAttachmentBits.set(colorIndex);
+ updateFloat32ColorAttachmentBits(
+ colorIndex, resource->getAttachmentFormat(binding, textureIndex).info);
+ }
+
+ // TODO(jmadill): ASSERT instead of checking the attachment exists in
+ // formsRenderingFeedbackLoopWith
+ bool enabled = (type != GL_NONE && getDrawBufferState(colorIndex) != GL_NONE);
+ mState.mEnabledDrawBuffers.set(colorIndex, enabled);
+ SetComponentTypeMask(getDrawbufferWriteType(colorIndex), colorIndex,
+ &mState.mDrawBufferTypeMask);
+ }
+ break;
+ }
+}
+
+void Framebuffer::updateAttachment(const Context *context,
+ FramebufferAttachment *attachment,
+ size_t dirtyBit,
+ angle::ObserverBinding *onDirtyBinding,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource,
+ GLsizei numViews,
+ GLuint baseViewIndex,
+ bool isMultiview)
+{
+ attachment->attach(context, type, binding, textureIndex, resource, numViews, baseViewIndex,
+ isMultiview);
+ mDirtyBits.set(dirtyBit);
+ mState.mResourceNeedsInit.set(dirtyBit, attachment->initState() == InitState::MayNeedInit);
+ onDirtyBinding->bind(resource);
+
+ invalidateCompletenessCache();
+}
+
+void Framebuffer::resetAttachment(const Context *context, GLenum binding)
+{
+ setAttachment(context, GL_NONE, binding, ImageIndex(), nullptr);
+}
+
+angle::Result Framebuffer::syncState(const Context *context)
+{
+ if (mDirtyBits.any())
+ {
+ mDirtyBitsGuard = mDirtyBits;
+ ANGLE_TRY(mImpl->syncState(context, mDirtyBits));
+ mDirtyBits.reset();
+ mDirtyBitsGuard.reset();
+ }
+ return angle::Result::Continue;
+}
+
+void Framebuffer::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
+{
+ if (message != angle::SubjectMessage::SubjectChanged)
+ {
+ // This can be triggered by SubImage calls for Textures.
+ if (message == angle::SubjectMessage::ContentsChanged)
+ {
+ mDirtyBits.set(DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + index);
+ onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
+ return;
+ }
+
+ // This can be triggered by the GL back-end TextureGL class.
+ ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged);
+ return;
+ }
+
+ ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(index));
+ mDirtyBits.set(index);
+
+ invalidateCompletenessCache();
+
+ FramebufferAttachment *attachment = getAttachmentFromSubjectIndex(index);
+
+ // Mark the appropriate init flag.
+ mState.mResourceNeedsInit.set(index, attachment->initState() == InitState::MayNeedInit);
+
+ // Update mFloat32ColorAttachmentBits Cache
+ if (index < DIRTY_BIT_COLOR_ATTACHMENT_MAX)
+ {
+ ASSERT(index != DIRTY_BIT_DEPTH_ATTACHMENT);
+ ASSERT(index != DIRTY_BIT_STENCIL_ATTACHMENT);
+ updateFloat32ColorAttachmentBits(index - DIRTY_BIT_COLOR_ATTACHMENT_0,
+ attachment->getFormat().info);
+ }
+}
+
+FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::SubjectIndex index)
+{
+ switch (index)
+ {
+ case DIRTY_BIT_DEPTH_ATTACHMENT:
+ return &mState.mDepthAttachment;
+ case DIRTY_BIT_STENCIL_ATTACHMENT:
+ return &mState.mStencilAttachment;
+ default:
+ size_t colorIndex = (index - DIRTY_BIT_COLOR_ATTACHMENT_0);
+ ASSERT(colorIndex < mState.mColorAttachments.size());
+ return &mState.mColorAttachments[colorIndex];
+ }
+}
+
+bool Framebuffer::formsRenderingFeedbackLoopWith(const Context *context) const
+{
+ const State &state = context->getState();
+ const Program *program = state.getProgram();
+
+ // TODO(jmadill): Default framebuffer feedback loops.
+ if (mState.mId == 0)
+ {
+ return false;
+ }
+
+ const FramebufferAttachment *depth = getDepthAttachment();
+ const FramebufferAttachment *stencil = getStencilAttachment();
+
+ const bool checkDepth = depth && depth->type() == GL_TEXTURE;
+ // Skip the feedback loop check for stencil if depth/stencil point to the same resource.
+ const bool checkStencil =
+ (stencil && stencil->type() == GL_TEXTURE) && (!depth || *stencil != *depth);
+
+ const gl::ActiveTextureMask &activeTextures = program->getActiveSamplersMask();
+ const gl::ActiveTexturePointerArray &textures = state.getActiveTexturesCache();
+
+ for (size_t textureUnit : activeTextures)
+ {
+ Texture *texture = textures[textureUnit];
+
+ if (texture == nullptr)
+ {
+ continue;
+ }
+
+ // Depth and stencil attachment form feedback loops
+ // Regardless of if enabled or masked.
+ if (checkDepth)
+ {
+ if (texture->id() == depth->id())
+ {
+ return true;
+ }
+ }
+
+ if (checkStencil)
+ {
+ if (texture->id() == stencil->id())
+ {
+ return true;
+ }
+ }
+
+ // Check if any color attachment forms a feedback loop.
+ for (size_t drawIndex : mColorAttachmentBits)
+ {
+ const FramebufferAttachment &attachment = mState.mColorAttachments[drawIndex];
+ ASSERT(attachment.isAttached());
+
+ if (attachment.isTextureWithId(texture->id()))
+ {
+ // TODO(jmadill): Check for appropriate overlap.
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID,
+ GLint copyTextureLevel,
+ GLint copyTextureLayer) const
+{
+ if (mState.mId == 0)
+ {
+ // It seems impossible to form a texture copying feedback loop with the default FBO.
+ return false;
+ }
+
+ const FramebufferAttachment *readAttachment = getReadColorAttachment();
+ ASSERT(readAttachment);
+
+ if (readAttachment->isTextureWithId(copyTextureID))
+ {
+ const auto &imageIndex = readAttachment->getTextureImageIndex();
+ if (imageIndex.getLevelIndex() == copyTextureLevel)
+ {
+ // Check 3D/Array texture layers.
+ return !imageIndex.hasLayer() || copyTextureLayer == ImageIndex::kEntireLevel ||
+ imageIndex.getLayerIndex() == copyTextureLayer;
+ }
+ }
+ return false;
+}
+
+GLint Framebuffer::getDefaultWidth() const
+{
+ return mState.getDefaultWidth();
+}
+
+GLint Framebuffer::getDefaultHeight() const
+{
+ return mState.getDefaultHeight();
+}
+
+GLint Framebuffer::getDefaultSamples() const
+{
+ return mState.getDefaultSamples();
+}
+
+bool Framebuffer::getDefaultFixedSampleLocations() const
+{
+ return mState.getDefaultFixedSampleLocations();
+}
+
+GLint Framebuffer::getDefaultLayers() const
+{
+ return mState.getDefaultLayers();
+}
+
+void Framebuffer::setDefaultWidth(const Context *context, GLint defaultWidth)
+{
+ mState.mDefaultWidth = defaultWidth;
+ mDirtyBits.set(DIRTY_BIT_DEFAULT_WIDTH);
+ invalidateCompletenessCache();
+}
+
+void Framebuffer::setDefaultHeight(const Context *context, GLint defaultHeight)
+{
+ mState.mDefaultHeight = defaultHeight;
+ mDirtyBits.set(DIRTY_BIT_DEFAULT_HEIGHT);
+ invalidateCompletenessCache();
+}
+
+void Framebuffer::setDefaultSamples(const Context *context, GLint defaultSamples)
+{
+ mState.mDefaultSamples = defaultSamples;
+ mDirtyBits.set(DIRTY_BIT_DEFAULT_SAMPLES);
+ invalidateCompletenessCache();
+}
+
+void Framebuffer::setDefaultFixedSampleLocations(const Context *context,
+ bool defaultFixedSampleLocations)
+{
+ mState.mDefaultFixedSampleLocations = defaultFixedSampleLocations;
+ mDirtyBits.set(DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS);
+ invalidateCompletenessCache();
+}
+
+void Framebuffer::setDefaultLayers(GLint defaultLayers)
+{
+ mState.mDefaultLayers = defaultLayers;
+ mDirtyBits.set(DIRTY_BIT_DEFAULT_LAYERS);
+}
+
+GLsizei Framebuffer::getNumViews() const
+{
+ return mState.getNumViews();
+}
+
+GLint Framebuffer::getBaseViewIndex() const
+{
+ return mState.getBaseViewIndex();
+}
+
+bool Framebuffer::isMultiview() const
+{
+ return mState.isMultiview();
+}
+
+bool Framebuffer::readDisallowedByMultiview() const
+{
+ return (mState.isMultiview() && mState.getNumViews() > 1);
+}
+
+angle::Result Framebuffer::ensureClearAttachmentsInitialized(const Context *context,
+ GLbitfield mask)
+{
+ const auto &glState = context->getState();
+ if (!context->isRobustResourceInitEnabled() || glState.isRasterizerDiscardEnabled())
+ {
+ return angle::Result::Continue;
+ }
+
+ const BlendState &blend = glState.getBlendState();
+ const DepthStencilState &depthStencil = glState.getDepthStencilState();
+
+ bool color = (mask & GL_COLOR_BUFFER_BIT) != 0 && !IsColorMaskedOut(blend);
+ bool depth = (mask & GL_DEPTH_BUFFER_BIT) != 0 && !IsDepthMaskedOut(depthStencil);
+ bool stencil = (mask & GL_STENCIL_BUFFER_BIT) != 0 && !IsStencilMaskedOut(depthStencil);
+
+ if (!color && !depth && !stencil)
+ {
+ return angle::Result::Continue;
+ }
+
+ if (partialClearNeedsInit(context, color, depth, stencil))
+ {
+ ANGLE_TRY(ensureDrawAttachmentsInitialized(context));
+ }
+
+ // If the impl encounters an error during a a full (non-partial) clear, the attachments will
+ // still be marked initialized. This simplifies design, allowing this method to be called before
+ // the clear.
+ markDrawAttachmentsInitialized(color, depth, stencil);
+
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::ensureClearBufferAttachmentsInitialized(const Context *context,
+ GLenum buffer,
+ GLint drawbuffer)
+{
+ if (!context->isRobustResourceInitEnabled() ||
+ context->getState().isRasterizerDiscardEnabled() || IsClearBufferMaskedOut(context, buffer))
+ {
+ return angle::Result::Continue;
+ }
+
+ if (partialBufferClearNeedsInit(context, buffer))
+ {
+ ANGLE_TRY(ensureBufferInitialized(context, buffer, drawbuffer));
+ }
+
+ // If the impl encounters an error during a a full (non-partial) clear, the attachments will
+ // still be marked initialized. This simplifies design, allowing this method to be called before
+ // the clear.
+ markBufferInitialized(buffer, drawbuffer);
+
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::ensureDrawAttachmentsInitialized(const Context *context)
+{
+ if (!context->isRobustResourceInitEnabled())
+ {
+ return angle::Result::Continue;
+ }
+
+ // Note: we don't actually filter by the draw attachment enum. Just init everything.
+ for (size_t bit : mState.mResourceNeedsInit)
+ {
+ switch (bit)
+ {
+ case DIRTY_BIT_DEPTH_ATTACHMENT:
+ ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
+ break;
+ case DIRTY_BIT_STENCIL_ATTACHMENT:
+ ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
+ break;
+ default:
+ ANGLE_TRY(InitAttachment(context, &mState.mColorAttachments[bit]));
+ break;
+ }
+ }
+
+ mState.mResourceNeedsInit.reset();
+ return angle::Result::Continue;
+}
+
+angle::Result Framebuffer::ensureReadAttachmentsInitialized(const Context *context)
+{
+ ASSERT(context->isRobustResourceInitEnabled());
+
+ if (mState.mResourceNeedsInit.none())
+ {
+ return angle::Result::Continue;
+ }
+
+ if (mState.mReadBufferState != GL_NONE)
+ {
+ size_t readIndex = mState.getReadIndex();
+ if (mState.mResourceNeedsInit[readIndex])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mColorAttachments[readIndex]));
+ mState.mResourceNeedsInit.reset(readIndex);
+ }
+ }
+
+ // Conservatively init depth since it can be read by BlitFramebuffer.
+ if (hasDepth())
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
+ }
+ }
+
+ // Conservatively init stencil since it can be read by BlitFramebuffer.
+ if (hasStencil())
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
+ }
+ }
+
+ return angle::Result::Continue;
+}
+
+void Framebuffer::markDrawAttachmentsInitialized(bool color, bool depth, bool stencil)
+{
+ // Mark attachments as initialized.
+ if (color)
+ {
+ for (auto colorIndex : mState.mEnabledDrawBuffers)
+ {
+ auto &colorAttachment = mState.mColorAttachments[colorIndex];
+ ASSERT(colorAttachment.isAttached());
+ colorAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(colorIndex);
+ }
+ }
+
+ if (depth && mState.mDepthAttachment.isAttached())
+ {
+ mState.mDepthAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
+ }
+
+ if (stencil && mState.mStencilAttachment.isAttached())
+ {
+ mState.mStencilAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
+ }
+}
+
+void Framebuffer::markBufferInitialized(GLenum bufferType, GLint bufferIndex)
+{
+ switch (bufferType)
+ {
+ case GL_COLOR:
+ {
+ ASSERT(bufferIndex < static_cast<GLint>(mState.mColorAttachments.size()));
+ if (mState.mColorAttachments[bufferIndex].isAttached())
+ {
+ mState.mColorAttachments[bufferIndex].setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(bufferIndex);
+ }
+ break;
+ }
+ case GL_DEPTH:
+ {
+ if (mState.mDepthAttachment.isAttached())
+ {
+ mState.mDepthAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
+ }
+ break;
+ }
+ case GL_STENCIL:
+ {
+ if (mState.mStencilAttachment.isAttached())
+ {
+ mState.mStencilAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
+ }
+ break;
+ }
+ case GL_DEPTH_STENCIL:
+ {
+ if (mState.mDepthAttachment.isAttached())
+ {
+ mState.mDepthAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
+ }
+ if (mState.mStencilAttachment.isAttached())
+ {
+ mState.mStencilAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+Box Framebuffer::getDimensions() const
+{
+ return mState.getDimensions();
+}
+
+Extents Framebuffer::getExtents() const
+{
+ return mState.getExtents();
+}
+
+angle::Result Framebuffer::ensureBufferInitialized(const Context *context,
+ GLenum bufferType,
+ GLint bufferIndex)
+{
+ ASSERT(context->isRobustResourceInitEnabled());
+
+ if (mState.mResourceNeedsInit.none())
+ {
+ return angle::Result::Continue;
+ }
+
+ switch (bufferType)
+ {
+ case GL_COLOR:
+ {
+ ASSERT(bufferIndex < static_cast<GLint>(mState.mColorAttachments.size()));
+ if (mState.mResourceNeedsInit[bufferIndex])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mColorAttachments[bufferIndex]));
+ mState.mResourceNeedsInit.reset(bufferIndex);
+ }
+ break;
+ }
+ case GL_DEPTH:
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
+ }
+ break;
+ }
+ case GL_STENCIL:
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
+ }
+ break;
+ }
+ case GL_DEPTH_STENCIL:
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
+ }
+ if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
+ mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ return angle::Result::Continue;
+}
+
+bool Framebuffer::partialBufferClearNeedsInit(const Context *context, GLenum bufferType)
+{
+ if (!context->isRobustResourceInitEnabled() || mState.mResourceNeedsInit.none())
+ {
+ return false;
+ }
+
+ switch (bufferType)
+ {
+ case GL_COLOR:
+ return partialClearNeedsInit(context, true, false, false);
+ case GL_DEPTH:
+ return partialClearNeedsInit(context, false, true, false);
+ case GL_STENCIL:
+ return partialClearNeedsInit(context, false, false, true);
+ case GL_DEPTH_STENCIL:
+ return partialClearNeedsInit(context, false, true, true);
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+} // namespace gl