| // | |
| // 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/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(); | |
| 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 Caps &caps, | |
| rx::GLImplFactory *factory, | |
| FramebufferID id, | |
| egl::ShareGroup *shareGroup) | |
| : mState(caps, id, shareGroup->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>(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); | |
| } | |
| if (caps.maxDrawBuffers > 1) | |
| { | |
| mDirtyBits.set(DIRTY_BIT_READ_BUFFER); | |
| } | |
| } | |
| Framebuffer::Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface) | |
| : mState(context->getShareGroup()->generateFramebufferSerial()), | |
| mImpl(surface->getImplementation()->createDefaultFramebuffer(context, mState)), | |
| mCachedStatus(FramebufferStatus::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, | |
| FramebufferAttachment::kDefaultRenderToTextureSamples); | |
| setReadSurface(context, readSurface); | |
| if (surface->getConfig()->depthSize > 0) | |
| { | |
| setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_DEPTH, ImageIndex(), surface, | |
| FramebufferAttachment::kDefaultNumViews, | |
| FramebufferAttachment::kDefaultBaseViewIndex, false, | |
| FramebufferAttachment::kDefaultRenderToTextureSamples); | |
| } | |
| if (surface->getConfig()->stencilSize > 0) | |
| { | |
| setAttachmentImpl(context, GL_FRAMEBUFFER_DEFAULT, GL_STENCIL, ImageIndex(), surface, | |
| FramebufferAttachment::kDefaultNumViews, | |
| FramebufferAttachment::kDefaultBaseViewIndex, false, | |
| FramebufferAttachment::kDefaultRenderToTextureSamples); | |
| } | |
| SetComponentTypeMask(getDrawbufferWriteType(0), 0, &mState.mDrawBufferTypeMask); | |
| 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); | |
| } | |
| Framebuffer::Framebuffer(const Context *context, | |
| rx::GLImplFactory *factory, | |
| egl::Surface *readSurface) | |
| : 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); | |
| setReadSurface(context, readSurface); | |
| } | |
| Framebuffer::~Framebuffer() | |
| { | |
| SafeDelete(mImpl); | |
| } | |
| void Framebuffer::onDestroy(const Context *context) | |
| { | |
| if (isDefault()) | |
| { | |
| mState.mDefaultFramebufferReadAttachment.detach(context, mState.mFramebufferSerial); | |
| mState.mDefaultFramebufferReadAttachmentInitialized = false; | |
| } | |
| 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); | |
| mImpl->destroy(context); | |
| } | |
| void Framebuffer::setReadSurface(const Context *context, egl::Surface *readSurface) | |
| { | |
| // 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); | |
| mDirtyBits.set(DIRTY_BIT_READ_BUFFER); | |
| } | |
| 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, 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 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::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; | |
| } | |
| 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.getProgramExecutable(); | |
| // 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; | |
| } | |
| } | |
| } // namespace gl |