// // 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.
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 (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)
{
// 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");
// 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;
}
}
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;
}
for (constauto &attachment : mColorAttachments)
{ if (hasMismatchedSize(attachment))
{ returnfalse;
}
}
if (hasMismatchedSize(mDepthAttachment))
{ returnfalse;
}
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));
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());
}
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);
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());
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);
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()); returntrue;
}
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();
}
}
// 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;
}
}
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);
} elseif (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;
}
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);
}
// 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.
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.
if (!glState.isRobustResourceInitEnabled())
{ returnfalse;
}
if (depth && context->getFrontendFeatures().forceDepthAttachmentInitOnClear.enabled)
{ returntrue;
}
// Scissors can affect clearing. // TODO(jmadill): Check for complete scissor overlap. if (glState.isScissorTestEnabled())
{ returntrue;
}
// 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())
{ returntrue;
}
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.
// 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());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.