summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/Framebuffer.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/angle/checkout/src/libANGLE/Framebuffer.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
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.cpp2727
1 files changed, 2727 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..08250b0b2d
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/Framebuffer.cpp
@@ -0,0 +1,2727 @@
+//
+// 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 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 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));
+ return angle::Result::Continue;
+}
+
+bool Framebuffer::hasValidDepthStencil() const
+{
+ return mState.getDepthStencilAttachment() != nullptr;
+}
+
+const gl::Offset &Framebuffer::getSurfaceTextureOffset() const
+{
+ return mState.getSurfaceTextureOffset();
+}
+
+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,
+ FramebufferAttachment::kDefaultRenderToTextureSamples);
+}
+
+void Framebuffer::setAttachmentMultisample(const Context *context,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource,
+ GLsizei samples)
+{
+ setAttachment(context, type, binding, textureIndex, resource,
+ FramebufferAttachment::kDefaultNumViews,
+ FramebufferAttachment::kDefaultBaseViewIndex, false, samples);
+}
+
+void Framebuffer::setAttachment(const Context *context,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource,
+ GLsizei numViews,
+ GLuint baseViewIndex,
+ bool isMultiview,
+ GLsizei samplesIn)
+{
+ GLsizei samples = samplesIn;
+ // Match the sample count to the attachment's sample count.
+ if (resource)
+ {
+ const InternalFormat *info = resource->getAttachmentFormat(binding, textureIndex).info;
+ ASSERT(info);
+ GLenum sizedInternalFormat = info->sizedInternalFormat;
+ const TextureCaps &formatCaps = context->getTextureCaps().get(sizedInternalFormat);
+ samples = formatCaps.getNearestSamples(samples);
+ }
+
+ // Context may be null in unit tests.
+ if (!context || !context->isWebGL1())
+ {
+ setAttachmentImpl(context, type, binding, textureIndex, resource, numViews, baseViewIndex,
+ isMultiview, samples);
+ return;
+ }
+
+ switch (binding)
+ {
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH_STENCIL_ATTACHMENT:
+ mState.mWebGLDepthStencilAttachment.attach(
+ context, type, binding, textureIndex, resource, numViews, baseViewIndex,
+ isMultiview, samples, mState.mFramebufferSerial);
+ break;
+ case GL_DEPTH:
+ case GL_DEPTH_ATTACHMENT:
+ mState.mWebGLDepthAttachment.attach(context, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview, samples,
+ mState.mFramebufferSerial);
+ break;
+ case GL_STENCIL:
+ case GL_STENCIL_ATTACHMENT:
+ mState.mWebGLStencilAttachment.attach(context, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview, samples,
+ mState.mFramebufferSerial);
+ break;
+ default:
+ setAttachmentImpl(context, type, binding, textureIndex, resource, numViews,
+ baseViewIndex, isMultiview, samples);
+ return;
+ }
+
+ commitWebGL1DepthStencilIfConsistent(context, numViews, baseViewIndex, isMultiview, samples);
+}
+
+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,
+ FramebufferAttachment::kDefaultRenderToTextureSamples);
+}
+
+void Framebuffer::commitWebGL1DepthStencilIfConsistent(const Context *context,
+ GLsizei numViews,
+ GLuint baseViewIndex,
+ bool isMultiview,
+ GLsizei samples)
+{
+ 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, samples);
+ setAttachmentImpl(context, GL_NONE, GL_STENCIL_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview, samples);
+ }
+ else if (mState.mWebGLStencilAttachment.isAttached())
+ {
+ const auto &stencil = mState.mWebGLStencilAttachment;
+ setAttachmentImpl(context, GL_NONE, GL_DEPTH_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview, samples);
+ setAttachmentImpl(context, stencil.type(), GL_STENCIL_ATTACHMENT,
+ getImageIndexIfTextureAttachment(stencil), stencil.getResource(),
+ numViews, baseViewIndex, isMultiview, samples);
+ }
+ else if (mState.mWebGLDepthStencilAttachment.isAttached())
+ {
+ const auto &depthStencil = mState.mWebGLDepthStencilAttachment;
+ setAttachmentImpl(context, depthStencil.type(), GL_DEPTH_ATTACHMENT,
+ getImageIndexIfTextureAttachment(depthStencil),
+ depthStencil.getResource(), numViews, baseViewIndex, isMultiview,
+ samples);
+ setAttachmentImpl(context, depthStencil.type(), GL_STENCIL_ATTACHMENT,
+ getImageIndexIfTextureAttachment(depthStencil),
+ depthStencil.getResource(), numViews, baseViewIndex, isMultiview,
+ samples);
+ }
+ else
+ {
+ setAttachmentImpl(context, GL_NONE, GL_DEPTH_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview, samples);
+ setAttachmentImpl(context, GL_NONE, GL_STENCIL_ATTACHMENT, ImageIndex(), nullptr, numViews,
+ baseViewIndex, isMultiview, samples);
+ }
+}
+
+void Framebuffer::setAttachmentImpl(const Context *context,
+ GLenum type,
+ GLenum binding,
+ const ImageIndex &textureIndex,
+ FramebufferAttachmentObject *resource,
+ GLsizei numViews,
+ GLuint baseViewIndex,
+ bool isMultiview,
+ GLsizei samples)
+{
+ switch (binding)
+ {
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH_STENCIL_ATTACHMENT:
+ updateAttachment(context, &mState.mDepthAttachment, DIRTY_BIT_DEPTH_ATTACHMENT,
+ &mDirtyDepthAttachmentBinding, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview, samples);
+ updateAttachment(context, &mState.mStencilAttachment, DIRTY_BIT_STENCIL_ATTACHMENT,
+ &mDirtyStencilAttachmentBinding, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview, samples);
+ break;
+
+ case GL_DEPTH:
+ case GL_DEPTH_ATTACHMENT:
+ updateAttachment(context, &mState.mDepthAttachment, DIRTY_BIT_DEPTH_ATTACHMENT,
+ &mDirtyDepthAttachmentBinding, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview, samples);
+ break;
+
+ case GL_STENCIL:
+ case GL_STENCIL_ATTACHMENT:
+ updateAttachment(context, &mState.mStencilAttachment, DIRTY_BIT_STENCIL_ATTACHMENT,
+ &mDirtyStencilAttachmentBinding, type, binding, textureIndex, resource,
+ numViews, baseViewIndex, isMultiview, samples);
+ break;
+
+ case GL_BACK:
+ updateAttachment(context, &mState.mColorAttachments[0], DIRTY_BIT_COLOR_ATTACHMENT_0,
+ &mDirtyColorAttachmentBindings[0], type, binding, textureIndex,
+ resource, numViews, baseViewIndex, isMultiview, samples);
+ mState.mColorAttachmentsMask.set(0);
+
+ 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, samples);
+
+ if (!resource)
+ {
+ mFloat32ColorAttachmentBits.reset(colorIndex);
+ mState.mColorAttachmentsMask.reset(colorIndex);
+ }
+ else
+ {
+ updateFloat32ColorAttachmentBits(
+ colorIndex, resource->getAttachmentFormat(binding, textureIndex).info);
+ mState.mColorAttachmentsMask.set(colorIndex);
+ }
+
+ 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,
+ GLsizei samples)
+{
+ attachment->attach(context, type, binding, textureIndex, resource, numViews, baseViewIndex,
+ isMultiview, samples, mState.mFramebufferSerial);
+ 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);
+}
+
+void Framebuffer::setWriteControlMode(SrgbWriteControlMode srgbWriteControlMode)
+{
+ if (srgbWriteControlMode != mState.getWriteControlMode())
+ {
+ mState.mSrgbWriteControlMode = srgbWriteControlMode;
+ mDirtyBits.set(DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE);
+ }
+}
+
+angle::Result Framebuffer::syncState(const Context *context,
+ GLenum framebufferBinding,
+ Command command) const
+{
+ if (mDirtyBits.any())
+ {
+ mDirtyBitsGuard = mDirtyBits;
+ ANGLE_TRY(mImpl->syncState(context, framebufferBinding, mDirtyBits, command));
+ 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;
+ }
+
+ // Swapchain changes should only result in color buffer changes.
+ if (message == angle::SubjectMessage::SwapchainImageChanged)
+ {
+ if (index < DIRTY_BIT_COLOR_ATTACHMENT_MAX)
+ {
+ mDirtyBits.set(DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + index);
+ onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
+ }
+ return;
+ }
+
+ ASSERT(message != angle::SubjectMessage::BindingChanged);
+
+ // This can be triggered by external changes to the default framebuffer.
+ if (message == angle::SubjectMessage::SurfaceChanged)
+ {
+ onStateChange(angle::SubjectMessage::SurfaceChanged);
+ return;
+ }
+
+ // This can be triggered by freeing TextureStorage in D3D back-end.
+ if (message == angle::SubjectMessage::StorageReleased)
+ {
+ mDirtyBits.set(index);
+ invalidateCompletenessCache();
+ 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 &glState = context->getState();
+ const ProgramExecutable *executable = glState.getLinkedProgramExecutable(context);
+
+ // In some error cases there may be no bound program or executable.
+ if (!executable)
+ return false;
+
+ const ActiveTextureMask &activeTextures = executable->getActiveSamplersMask();
+ const ActiveTextureTypeArray &textureTypes = executable->getActiveSamplerTypes();
+
+ for (size_t textureIndex : activeTextures)
+ {
+ unsigned int uintIndex = static_cast<unsigned int>(textureIndex);
+ Texture *texture = glState.getSamplerTexture(uintIndex, textureTypes[textureIndex]);
+ const Sampler *sampler = glState.getSampler(uintIndex);
+ if (texture && texture->isSamplerComplete(context, sampler) &&
+ texture->isBoundToFramebuffer(mState.mFramebufferSerial))
+ {
+ // Check for level overlap.
+ for (const FramebufferAttachment &attachment : mState.mColorAttachments)
+ {
+ if (AttachmentOverlapsWithTexture(attachment, texture, sampler))
+ {
+ return true;
+ }
+ }
+
+ if (AttachmentOverlapsWithTexture(mState.mDepthAttachment, texture, sampler))
+ {
+ return true;
+ }
+
+ if (AttachmentOverlapsWithTexture(mState.mStencilAttachment, texture, sampler))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Framebuffer::formsCopyingFeedbackLoopWith(TextureID copyTextureID,
+ GLint copyTextureLevel,
+ GLint copyTextureLayer) const
+{
+ if (mState.isDefault())
+ {
+ // 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();
+}
+
+bool Framebuffer::getFlipY() const
+{
+ return mState.getFlipY();
+}
+
+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);
+}
+
+void Framebuffer::setFlipY(bool flipY)
+{
+ mState.mFlipY = flipY;
+ mDirtyBits.set(DIRTY_BIT_FLIP_Y);
+ invalidateCompletenessCache();
+}
+
+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 DepthStencilState &depthStencil = glState.getDepthStencilState();
+
+ bool color = (mask & GL_COLOR_BUFFER_BIT) != 0 && !glState.allActiveDrawBufferChannelsMasked();
+ bool depth = (mask & GL_DEPTH_BUFFER_BIT) != 0 && !depthStencil.isDepthMaskedOut();
+ bool stencil = (mask & GL_STENCIL_BUFFER_BIT) != 0 && !depthStencil.isStencilMaskedOut();
+
+ 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() ||
+ context->isClearBufferMaskedOut(buffer, drawbuffer))
+ {
+ 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)
+ {
+ if (isDefault())
+ {
+ if (!mState.mDefaultFramebufferReadAttachmentInitialized)
+ {
+ ANGLE_TRY(InitAttachment(context, &mState.mDefaultFramebufferReadAttachment));
+ mState.mDefaultFramebufferReadAttachmentInitialized = true;
+ }
+ }
+ else
+ {
+ 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;
+ }
+}
+
+PixelLocalStorage &Framebuffer::getPixelLocalStorage(const Context *context)
+{
+ if (!mPixelLocalStorage)
+ {
+ mPixelLocalStorage = PixelLocalStorage::Make(context);
+ }
+ return *mPixelLocalStorage.get();
+}
+
+std::unique_ptr<PixelLocalStorage> Framebuffer::detachPixelLocalStorage()
+{
+ return std::move(mPixelLocalStorage);
+}
+} // namespace gl