Commit bccb0d56 by Geoff Lang Committed by Commit Bot

Add messages for framebuffer completeness errors.

This also creates a common code path for all framebuffer completeness errors (FramebufferStatus::Incomplete) which helps for adding a debug breakpoint. Bug: angleproject:5949 Change-Id: Ib102dbf86e020777e56c6dc6b78dda8ebdba2127 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2888110 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent e354ff1a
...@@ -6447,7 +6447,7 @@ GLenum Context::checkFramebufferStatus(GLenum target) ...@@ -6447,7 +6447,7 @@ GLenum Context::checkFramebufferStatus(GLenum target)
{ {
Framebuffer *framebuffer = mState.getTargetFramebuffer(target); Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer); ASSERT(framebuffer);
return framebuffer->checkStatus(this); return framebuffer->checkStatus(this).status;
} }
void Context::compileShader(ShaderProgramID shader) void Context::compileShader(ShaderProgramID shader)
......
...@@ -143,6 +143,41 @@ MSG kFragDataBindingIndexOutOfRange = "Fragment output color index must be zero ...@@ -143,6 +143,41 @@ MSG kFragDataBindingIndexOutOfRange = "Fragment output color index must be zero
MSG kFragmentInputTypeNotFloatingPoint = "Fragment input type is not a floating point scalar or vector."; MSG kFragmentInputTypeNotFloatingPoint = "Fragment input type is not a floating point scalar or vector.";
MSG kFramebufferIncomplete = "Framebuffer is incomplete."; MSG kFramebufferIncomplete = "Framebuffer is incomplete.";
MSG kFramebufferIncompleteAttachment = "Attachment type must be compatible with attachment object."; MSG kFramebufferIncompleteAttachment = "Attachment type must be compatible with attachment object.";
MSG kFramebufferIncompleteAttachmentDepthGreaterThanMaxLayers = "Framebuffer is incomplete: Attachment depth is greater than MAX_FRAMEBUFFER_LAYERS.";
MSG kFramebufferIncompleteAttachmentInconsistantBitPlanes = "Framebuffer is incomplete: Attachments have inconsistent bit plane counts.";
MSG kFramebufferIncompleteAttachmentLayerGreaterThanDepth = "Framebuffer is incomplete: Attachment layer is greater than texture layer count.";
MSG kFramebufferIncompleteAttachmentLevelNotBaseLevelForIncompleteMipTexture = "Framebuffer is incomplete: Attachment level not equal to the base level and the texture is not mipmap complete.";
MSG kFramebufferIncompleteAttachmentLevelOutOfBaseMaxLevelRange = "Framebuffer is incomplete: Attachment level is not in the [base level, max level] range.";
MSG kFramebufferIncompleteAttachmentNoDepthBitsInDepthBuffer = "Framebuffer is incomplete: Depth attachment has no depth bits.";
MSG kFramebufferIncompleteAttachmentNoStencilBitsInStencilBuffer = "Framebuffer is incomplete: Stencil attachment has no stencil bits.";
MSG kFramebufferIncompleteAttachmentNotCubeComplete = "Framebuffer is incomplete: Attachment is an incomplete cube map.";
MSG kFramebufferIncompleteAttachmentNotRenderable = "Framebuffer is incomplete: Attachment is not renderable.";
MSG kFramebufferIncompleteAttachmentSamplesGreaterThanMaxSupportedSamples = "Framebuffer is incomplete: Attachment samples are greater than the maximum supported samples for this format.";
MSG kFramebufferIncompleteAttachmentsNotUnique = "Framebuffer is incomplete: All attachments must be unique.";
MSG kFramebufferIncompleteAttachmentWebGLDepthBufferHasStencilBits = "Framebuffer is incomplete: Stencil attachment has depth bits.";
MSG kFramebufferIncompleteAttachmentWebGLDepthStencilNoDepthOrStencilBits = "Framebuffer is incomplete: Depth stencil attachment has no depth bits or no stencil bits.";
MSG kFramebufferIncompleteAttachmentWebGLStencilBufferHasDepthBits = "Framebuffer is incomplete: Stencil attachment has depth bits.";
MSG kFramebufferIncompleteAttachmentZeroSize = "Framebuffer is incomplete: Attachment has zero size.";
MSG kFramebufferIncompleteDefaultZeroSize = "Framebuffer is incomplete: No attachments and default size is zero.";
MSG kFramebufferIncompleteDepthAndStencilBuffersNotTheSame = "Framebuffer is incomplete: Depth and stencil attachments are not the same.";
MSG kFramebufferIncompleteDepthStencilInColorBuffer = "Framebuffer is incomplete: Depth stencil texture in color attachment.";
MSG kFramebufferIncompleteDriverUnsupported = "Framebuffer is incomplete: Driver does not support this framebuffer configuration.";
MSG kFramebufferIncompleteInconsistantAttachmentSizes = "Framebuffer is incomplete: Attachments are not all the same size.";
MSG kFramebufferIncompleteInternalError = "Framebuffer is incomplete: Internal error.";
MSG kFramebufferIncompleteMismatchedLayeredAttachments = "Framebuffer is incomplete: If one attachment is layered, all must be layered.";
MSG kFramebufferIncompleteMismatchedLayeredTexturetypes = "Framebuffer is incomplete: If an attachments are layered, they must all be the same texture type.";
MSG kFramebufferIncompleteMultisampleDepthStencilSampleCountDivisibleByColorSampleCount = "Framebuffer is incomplete: Depth stencil sample count must be divisible by the color sample count.";
MSG kFramebufferIncompleteMultisampleInconsistentFixedSampleLocations = "Framebuffer is incomplete: Attachments have inconsistent fixed sample locations.";
MSG kFramebufferIncompleteMultisampleInconsistentSampleCounts = "Framebuffer is incomplete: Attachments have different sample counts.";
MSG kFramebufferIncompleteMultisampleNonFixedSamplesWithRenderbuffers = "Framebuffer is incomplete: All textures must have fixed samples if paired with multisample renderbuffers.";
MSG kFramebufferIncompleteMultiviewBaseViewMismatch = "Framebuffer is incomplete: Attachments have inconsistent multiview base view.";
MSG kFramebufferIncompleteMultiviewMismatch = "Framebuffer is incomplete: Attachments have inconsistent multiview enabled state.";
MSG kFramebufferIncompleteMultiviewViewsMismatch = "Framebuffer is incomplete: Attachments have inconsistent multiview view counts.";
MSG kFramebufferIncompleteSurfaceless = "Framebuffer is incomplete: Framebuffer is surfaceless.";
MSG kFramebufferIncompleteUnsupportedMissmatchedDimensions = "Framebuffer is incomplete: Mismatched attachment sizes are unsupported.";
MSG kFramebufferIncompleteUnsupportedNonUniqueAttachments = "Framebuffer is incomplete: Non-unique attachments are unsupported.";
MSG kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers = "Framebuffer is incomplete: Separate depth and stencil buffers are unsupported.";
MSG kFramebufferIncompleteWebGLDepthStencilInconsistant = "Framebuffer is incomplete: WebGL depth stencil state is inconsistent.";
MSG kFramebufferTextureInvalidLayer = "Layer invalid for framebuffer texture attachment."; MSG kFramebufferTextureInvalidLayer = "Layer invalid for framebuffer texture attachment.";
MSG kFramebufferTextureInvalidMipLevel = "Mip level invalid for framebuffer texture attachment."; MSG kFramebufferTextureInvalidMipLevel = "Mip level invalid for framebuffer texture attachment.";
MSG kFramebufferTextureLayerIncorrectTextureType = "Texture is not a three-dimensional or two-dimensionsal array texture."; MSG kFramebufferTextureLayerIncorrectTextureType = "Texture is not a three-dimensional or two-dimensionsal array texture.";
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "libANGLE/Config.h" #include "libANGLE/Config.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Display.h" #include "libANGLE/Display.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Renderbuffer.h" #include "libANGLE/Renderbuffer.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
...@@ -35,40 +36,48 @@ namespace gl ...@@ -35,40 +36,48 @@ namespace gl
namespace namespace
{ {
bool CheckMultiviewStateMatchesForCompleteness(const FramebufferAttachment *firstAttachment, FramebufferStatus CheckMultiviewStateMatchesForCompleteness(
const FramebufferAttachment *secondAttachment) const FramebufferAttachment *firstAttachment,
const FramebufferAttachment *secondAttachment)
{ {
ASSERT(firstAttachment && secondAttachment); ASSERT(firstAttachment && secondAttachment);
ASSERT(firstAttachment->isAttached() && secondAttachment->isAttached()); ASSERT(firstAttachment->isAttached() && secondAttachment->isAttached());
if (firstAttachment->getNumViews() != secondAttachment->getNumViews()) if (firstAttachment->getNumViews() != secondAttachment->getNumViews())
{ {
return false; return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR,
err::kFramebufferIncompleteMultiviewViewsMismatch);
} }
if (firstAttachment->getBaseViewIndex() != secondAttachment->getBaseViewIndex()) if (firstAttachment->getBaseViewIndex() != secondAttachment->getBaseViewIndex())
{ {
return false; return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR,
err::kFramebufferIncompleteMultiviewBaseViewMismatch);
} }
if (firstAttachment->isMultiview() != secondAttachment->isMultiview()) if (firstAttachment->isMultiview() != secondAttachment->isMultiview())
{ {
return false; return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR,
err::kFramebufferIncompleteMultiviewMismatch);
} }
return true;
return FramebufferStatus::Complete();
} }
bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttachment &attachment) FramebufferStatus CheckAttachmentCompleteness(const Context *context,
const FramebufferAttachment &attachment)
{ {
ASSERT(attachment.isAttached()); ASSERT(attachment.isAttached());
const Extents &size = attachment.getSize(); const Extents &size = attachment.getSize();
if (size.width == 0 || size.height == 0) if (size.width == 0 || size.height == 0)
{ {
return false; return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentZeroSize);
} }
if (!attachment.isRenderable(context)) if (!attachment.isRenderable(context))
{ {
return false; return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentNotRenderable);
} }
if (attachment.type() == GL_TEXTURE) if (attachment.type() == GL_TEXTURE)
...@@ -81,7 +90,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach ...@@ -81,7 +90,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach
{ {
if (attachment.layer() >= size.depth) if (attachment.layer() >= size.depth)
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentLayerGreaterThanDepth);
} }
} }
// If <image> is a three-dimensional texture or a two-dimensional array texture and the // If <image> is a three-dimensional texture or a two-dimensional array texture and the
...@@ -91,7 +102,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach ...@@ -91,7 +102,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach
{ {
if (size.depth >= context->getCaps().maxFramebufferLayers) if (size.depth >= context->getCaps().maxFramebufferLayers)
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentDepthGreaterThanMaxLayers);
} }
} }
...@@ -104,7 +117,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach ...@@ -104,7 +117,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach
if (texture->getType() == TextureType::CubeMap && if (texture->getType() == TextureType::CubeMap &&
!texture->getTextureState().isCubeComplete()) !texture->getTextureState().isCubeComplete())
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentNotCubeComplete);
} }
if (!texture->getImmutableFormat()) if (!texture->getImmutableFormat())
...@@ -121,7 +136,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach ...@@ -121,7 +136,9 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach
if (attachmentMipLevel < texture->getBaseLevel() || if (attachmentMipLevel < texture->getBaseLevel() ||
attachmentMipLevel > texture->getMipmapMaxLevel()) attachmentMipLevel > texture->getMipmapMaxLevel())
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentLevelOutOfBaseMaxLevelRange);
} }
// Form the ES 3.0 spec, pg 213/214: // Form the ES 3.0 spec, pg 213/214:
...@@ -132,18 +149,20 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach ...@@ -132,18 +149,20 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach
// a cubemap texture, the texture must also be cube complete. // a cubemap texture, the texture must also be cube complete.
if (attachmentMipLevel != texture->getBaseLevel() && !texture->isMipmapComplete()) if (attachmentMipLevel != texture->getBaseLevel() && !texture->isMipmapComplete())
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentLevelNotBaseLevelForIncompleteMipTexture);
} }
} }
} }
return true; return FramebufferStatus::Complete();
} }
bool CheckAttachmentSampleCounts(const Context *context, FramebufferStatus CheckAttachmentSampleCounts(const Context *context,
GLsizei currAttachmentSamples, GLsizei currAttachmentSamples,
GLsizei samples, GLsizei samples,
bool colorAttachment) bool colorAttachment)
{ {
if (currAttachmentSamples != samples) if (currAttachmentSamples != samples)
{ {
...@@ -151,7 +170,9 @@ bool CheckAttachmentSampleCounts(const Context *context, ...@@ -151,7 +170,9 @@ bool CheckAttachmentSampleCounts(const Context *context,
{ {
// APPLE_framebuffer_multisample, which EXT_draw_buffers refers to, requires that // 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. // all color attachments have the same number of samples for the FBO to be complete.
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
err::kFramebufferIncompleteMultisampleInconsistentSampleCounts);
} }
else else
{ {
...@@ -159,24 +180,30 @@ bool CheckAttachmentSampleCounts(const Context *context, ...@@ -159,24 +180,30 @@ bool CheckAttachmentSampleCounts(const Context *context,
// when its depth or stencil samples are a multiple of the number of color samples. // when its depth or stencil samples are a multiple of the number of color samples.
if (!context->getExtensions().framebufferMixedSamples) if (!context->getExtensions().framebufferMixedSamples)
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
err::kFramebufferIncompleteMultisampleInconsistentSampleCounts);
} }
if ((currAttachmentSamples % std::max(samples, 1)) != 0) if ((currAttachmentSamples % std::max(samples, 1)) != 0)
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
err::
kFramebufferIncompleteMultisampleDepthStencilSampleCountDivisibleByColorSampleCount);
} }
} }
} }
return true;
return FramebufferStatus::Complete();
} }
bool CheckAttachmentSampleCompleteness(const Context *context, FramebufferStatus CheckAttachmentSampleCompleteness(const Context *context,
const FramebufferAttachment &attachment, const FramebufferAttachment &attachment,
bool colorAttachment, bool colorAttachment,
Optional<int> *samples, Optional<int> *samples,
Optional<bool> *fixedSampleLocations, Optional<bool> *fixedSampleLocations,
Optional<int> *renderToTextureSamples) Optional<int> *renderToTextureSamples)
{ {
ASSERT(attachment.isAttached()); ASSERT(attachment.isAttached());
...@@ -188,14 +215,18 @@ bool CheckAttachmentSampleCompleteness(const Context *context, ...@@ -188,14 +215,18 @@ bool CheckAttachmentSampleCompleteness(const Context *context,
const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat); const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat);
if (static_cast<GLuint>(attachment.getSamples()) > formatCaps.getMaxSamples()) if (static_cast<GLuint>(attachment.getSamples()) > formatCaps.getMaxSamples())
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
err::kFramebufferIncompleteAttachmentSamplesGreaterThanMaxSupportedSamples);
} }
const ImageIndex &attachmentImageIndex = attachment.getTextureImageIndex(); const ImageIndex &attachmentImageIndex = attachment.getTextureImageIndex();
bool fixedSampleloc = texture->getAttachmentFixedSampleLocations(attachmentImageIndex); bool fixedSampleloc = texture->getAttachmentFixedSampleLocations(attachmentImageIndex);
if (fixedSampleLocations->valid() && fixedSampleloc != fixedSampleLocations->value()) if (fixedSampleLocations->valid() && fixedSampleloc != fixedSampleLocations->value())
{ {
return false; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
err::kFramebufferIncompleteMultisampleInconsistentFixedSampleLocations);
} }
else else
{ {
...@@ -209,10 +240,12 @@ bool CheckAttachmentSampleCompleteness(const Context *context, ...@@ -209,10 +240,12 @@ bool CheckAttachmentSampleCompleteness(const Context *context,
if (renderToTextureSamples->value() != if (renderToTextureSamples->value() !=
FramebufferAttachment::kDefaultRenderToTextureSamples) FramebufferAttachment::kDefaultRenderToTextureSamples)
{ {
if (!CheckAttachmentSampleCounts(context, attachment.getRenderToTextureSamples(), FramebufferStatus sampleCountStatus =
renderToTextureSamples->value(), colorAttachment)) CheckAttachmentSampleCounts(context, attachment.getRenderToTextureSamples(),
renderToTextureSamples->value(), colorAttachment);
if (!sampleCountStatus.isComplete())
{ {
return false; return sampleCountStatus;
} }
} }
} }
...@@ -227,10 +260,12 @@ bool CheckAttachmentSampleCompleteness(const Context *context, ...@@ -227,10 +260,12 @@ bool CheckAttachmentSampleCompleteness(const Context *context,
if (renderToTextureSamples->value() == if (renderToTextureSamples->value() ==
FramebufferAttachment::kDefaultRenderToTextureSamples) FramebufferAttachment::kDefaultRenderToTextureSamples)
{ {
if (!CheckAttachmentSampleCounts(context, attachment.getSamples(), samples->value(),
colorAttachment)) FramebufferStatus sampleCountStatus = CheckAttachmentSampleCounts(
context, attachment.getSamples(), samples->value(), colorAttachment);
if (!sampleCountStatus.isComplete())
{ {
return false; return sampleCountStatus;
} }
} }
} }
...@@ -239,7 +274,7 @@ bool CheckAttachmentSampleCompleteness(const Context *context, ...@@ -239,7 +274,7 @@ bool CheckAttachmentSampleCompleteness(const Context *context,
*samples = attachment.getSamples(); *samples = attachment.getSamples();
} }
return true; return FramebufferStatus::Complete();
} }
// Needed to index into the attachment arrays/bitsets. // Needed to index into the attachment arrays/bitsets.
...@@ -284,8 +319,32 @@ bool AttachmentOverlapsWithTexture(const FramebufferAttachment &attachment, ...@@ -284,8 +319,32 @@ bool AttachmentOverlapsWithTexture(const FramebufferAttachment &attachment,
return attachmentLevel >= textureEffectiveBaseLevel && attachmentLevel <= textureMaxLevel; return attachmentLevel >= textureEffectiveBaseLevel && attachmentLevel <= textureMaxLevel;
} }
} // anonymous namespace } // 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. // This constructor is only used for default framebuffers.
FramebufferState::FramebufferState(rx::Serial serial) FramebufferState::FramebufferState(rx::Serial serial)
: mId(Framebuffer::kDefaultDrawFramebufferHandle), : mId(Framebuffer::kDefaultDrawFramebufferHandle),
...@@ -745,7 +804,7 @@ Framebuffer::Framebuffer(const Caps &caps, ...@@ -745,7 +804,7 @@ Framebuffer::Framebuffer(const Caps &caps,
Framebuffer::Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface) Framebuffer::Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface)
: mState(context->getShareGroup()->generateFramebufferSerial()), : mState(context->getShareGroup()->generateFramebufferSerial()),
mImpl(surface->getImplementation()->createDefaultFramebuffer(context, mState)), mImpl(surface->getImplementation()->createDefaultFramebuffer(context, mState)),
mCachedStatus(GL_FRAMEBUFFER_COMPLETE), mCachedStatus(FramebufferStatus::Complete()),
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT), mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT) mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{ {
...@@ -787,7 +846,8 @@ Framebuffer::Framebuffer(const Context *context, ...@@ -787,7 +846,8 @@ Framebuffer::Framebuffer(const Context *context,
egl::Surface *readSurface) egl::Surface *readSurface)
: mState(context->getShareGroup()->generateFramebufferSerial()), : mState(context->getShareGroup()->generateFramebufferSerial()),
mImpl(factory->createFramebuffer(mState)), mImpl(factory->createFramebuffer(mState)),
mCachedStatus(GL_FRAMEBUFFER_UNDEFINED_OES), mCachedStatus(FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNDEFINED_OES,
err::kFramebufferIncompleteSurfaceless)),
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT), mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT) mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{ {
...@@ -1116,14 +1176,14 @@ void Framebuffer::invalidateCompletenessCache() ...@@ -1116,14 +1176,14 @@ void Framebuffer::invalidateCompletenessCache()
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
} }
GLenum Framebuffer::checkStatusImpl(const Context *context) const const FramebufferStatus &Framebuffer::checkStatusImpl(const Context *context) const
{ {
ASSERT(!isDefault()); ASSERT(!isDefault());
ASSERT(hasAnyDirtyBit() || !mCachedStatus.valid()); ASSERT(hasAnyDirtyBit() || !mCachedStatus.valid());
mCachedStatus = checkStatusWithGLFrontEnd(context); mCachedStatus = checkStatusWithGLFrontEnd(context);
if (mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE) if (mCachedStatus.value().isComplete())
{ {
// We can skip syncState on several back-ends. // We can skip syncState on several back-ends.
if (mImpl->shouldSyncStateBeforeCheckStatus()) if (mImpl->shouldSyncStateBeforeCheckStatus())
...@@ -1133,20 +1193,19 @@ GLenum Framebuffer::checkStatusImpl(const Context *context) const ...@@ -1133,20 +1193,19 @@ GLenum Framebuffer::checkStatusImpl(const Context *context) const
angle::Result err = syncState(context, GL_FRAMEBUFFER, Command::Other); angle::Result err = syncState(context, GL_FRAMEBUFFER, Command::Other);
if (err != angle::Result::Continue) if (err != angle::Result::Continue)
{ {
return 0; mCachedStatus =
FramebufferStatus::Incomplete(0, err::kFramebufferIncompleteInternalError);
return mCachedStatus.value();
} }
} }
if (!mImpl->checkStatus(context)) mCachedStatus = mImpl->checkStatus(context);
{
mCachedStatus = GL_FRAMEBUFFER_UNSUPPORTED;
}
} }
return mCachedStatus.value(); return mCachedStatus.value();
} }
GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const FramebufferStatus Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
{ {
const State &state = context->getState(); const State &state = context->getState();
...@@ -1168,21 +1227,27 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1168,21 +1227,27 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
{ {
if (colorAttachment.isAttached()) if (colorAttachment.isAttached())
{ {
if (!CheckAttachmentCompleteness(context, colorAttachment)) FramebufferStatus attachmentCompleteness =
CheckAttachmentCompleteness(context, colorAttachment);
if (!attachmentCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return attachmentCompleteness;
} }
const InternalFormat &format = *colorAttachment.getFormat().info; const InternalFormat &format = *colorAttachment.getFormat().info;
if (format.depthBits > 0 || format.stencilBits > 0) if (format.depthBits > 0 || format.stencilBits > 0)
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteDepthStencilInColorBuffer);
} }
if (!CheckAttachmentSampleCompleteness(context, colorAttachment, true, &samples, FramebufferStatus attachmentSampleCompleteness =
&fixedSampleLocations, &renderToTextureSamples)) CheckAttachmentSampleCompleteness(context, colorAttachment, true, &samples,
&fixedSampleLocations, &renderToTextureSamples);
if (!attachmentSampleCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; return attachmentSampleCompleteness;
} }
// in GLES 2.0, all color attachments attachments must have the same number of bitplanes // in GLES 2.0, all color attachments attachments must have the same number of bitplanes
...@@ -1193,7 +1258,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1193,7 +1258,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
{ {
if (format.pixelBytes != colorbufferSize.value()) if (format.pixelBytes != colorbufferSize.value())
{ {
return GL_FRAMEBUFFER_UNSUPPORTED; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
err::kFramebufferIncompleteAttachmentInconsistantBitPlanes);
} }
} }
else else
...@@ -1202,9 +1269,11 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1202,9 +1269,11 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
} }
} }
if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment, &colorAttachment)) FramebufferStatus attachmentMultiviewCompleteness =
CheckMultiviewStateMatchesForCompleteness(firstAttachment, &colorAttachment);
if (!attachmentMultiviewCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR; return attachmentMultiviewCompleteness;
} }
hasRenderbuffer = hasRenderbuffer || (colorAttachment.type() == GL_RENDERBUFFER); hasRenderbuffer = hasRenderbuffer || (colorAttachment.type() == GL_RENDERBUFFER);
...@@ -1227,7 +1296,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1227,7 +1296,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
ASSERT(isLayered.valid()); ASSERT(isLayered.valid());
if (isLayered.value() != colorAttachment.isLayered()) if (isLayered.value() != colorAttachment.isLayered())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
err::kFramebufferIncompleteMismatchedLayeredAttachments);
} }
else if (isLayered.value()) else if (isLayered.value())
{ {
...@@ -1235,7 +1306,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1235,7 +1306,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
if (colorAttachmentsTextureType.value() != if (colorAttachmentsTextureType.value() !=
colorAttachment.getTextureImageIndex().getType()) colorAttachment.getTextureImageIndex().getType())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
err::kFramebufferIncompleteMismatchedLayeredTexturetypes);
} }
} }
} }
...@@ -1245,26 +1318,34 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1245,26 +1318,34 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
const FramebufferAttachment &depthAttachment = mState.mDepthAttachment; const FramebufferAttachment &depthAttachment = mState.mDepthAttachment;
if (depthAttachment.isAttached()) if (depthAttachment.isAttached())
{ {
if (!CheckAttachmentCompleteness(context, depthAttachment)) FramebufferStatus attachmentCompleteness =
CheckAttachmentCompleteness(context, depthAttachment);
if (!attachmentCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return attachmentCompleteness;
} }
const InternalFormat &format = *depthAttachment.getFormat().info; const InternalFormat &format = *depthAttachment.getFormat().info;
if (format.depthBits == 0) if (format.depthBits == 0)
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentNoDepthBitsInDepthBuffer);
} }
if (!CheckAttachmentSampleCompleteness(context, depthAttachment, false, &samples, FramebufferStatus attachmentSampleCompleteness =
&fixedSampleLocations, &renderToTextureSamples)) CheckAttachmentSampleCompleteness(context, depthAttachment, false, &samples,
&fixedSampleLocations, &renderToTextureSamples);
if (!attachmentSampleCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; return attachmentSampleCompleteness;
} }
if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment, &depthAttachment)) FramebufferStatus attachmentMultiviewCompleteness =
CheckMultiviewStateMatchesForCompleteness(firstAttachment, &depthAttachment);
if (!attachmentMultiviewCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR; return attachmentMultiviewCompleteness;
} }
hasRenderbuffer = hasRenderbuffer || (depthAttachment.type() == GL_RENDERBUFFER); hasRenderbuffer = hasRenderbuffer || (depthAttachment.type() == GL_RENDERBUFFER);
...@@ -1282,7 +1363,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1282,7 +1363,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
ASSERT(isLayered.valid()); ASSERT(isLayered.valid());
if (isLayered.value() != depthAttachment.isLayered()) if (isLayered.value() != depthAttachment.isLayered())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
err::kFramebufferIncompleteMismatchedLayeredAttachments);
} }
} }
} }
...@@ -1290,26 +1373,34 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1290,26 +1373,34 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
const FramebufferAttachment &stencilAttachment = mState.mStencilAttachment; const FramebufferAttachment &stencilAttachment = mState.mStencilAttachment;
if (stencilAttachment.isAttached()) if (stencilAttachment.isAttached())
{ {
if (!CheckAttachmentCompleteness(context, stencilAttachment)) FramebufferStatus attachmentCompleteness =
CheckAttachmentCompleteness(context, stencilAttachment);
if (!attachmentCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return attachmentCompleteness;
} }
const InternalFormat &format = *stencilAttachment.getFormat().info; const InternalFormat &format = *stencilAttachment.getFormat().info;
if (format.stencilBits == 0) if (format.stencilBits == 0)
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentNoStencilBitsInStencilBuffer);
} }
if (!CheckAttachmentSampleCompleteness(context, stencilAttachment, false, &samples, FramebufferStatus attachmentSampleCompleteness =
&fixedSampleLocations, &renderToTextureSamples)) CheckAttachmentSampleCompleteness(context, stencilAttachment, false, &samples,
&fixedSampleLocations, &renderToTextureSamples);
if (!attachmentSampleCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; return attachmentSampleCompleteness;
} }
if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment, &stencilAttachment)) FramebufferStatus attachmentMultiviewCompleteness =
CheckMultiviewStateMatchesForCompleteness(firstAttachment, &stencilAttachment);
if (!attachmentMultiviewCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR; return attachmentMultiviewCompleteness;
} }
hasRenderbuffer = hasRenderbuffer || (stencilAttachment.type() == GL_RENDERBUFFER); hasRenderbuffer = hasRenderbuffer || (stencilAttachment.type() == GL_RENDERBUFFER);
...@@ -1327,7 +1418,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1327,7 +1418,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
ASSERT(isLayered.valid()); ASSERT(isLayered.valid());
if (isLayered.value() != stencilAttachment.isLayered()) if (isLayered.value() != stencilAttachment.isLayered())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT,
err::kFramebufferIncompleteMismatchedLayeredAttachments);
} }
} }
} }
...@@ -1336,7 +1429,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1336,7 +1429,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
if (state.getClientMajorVersion() >= 3 && depthAttachment.isAttached() && if (state.getClientMajorVersion() >= 3 && depthAttachment.isAttached() &&
stencilAttachment.isAttached() && stencilAttachment != depthAttachment) stencilAttachment.isAttached() && stencilAttachment != depthAttachment)
{ {
return GL_FRAMEBUFFER_UNSUPPORTED; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
err::kFramebufferIncompleteDepthAndStencilBuffersNotTheSame);
} }
// Special additional validation for WebGL 1 DEPTH/STENCIL/DEPTH_STENCIL. // Special additional validation for WebGL 1 DEPTH/STENCIL/DEPTH_STENCIL.
...@@ -1344,7 +1439,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1344,7 +1439,9 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
{ {
if (!mState.mWebGLDepthStencilConsistent) if (!mState.mWebGLDepthStencilConsistent)
{ {
return GL_FRAMEBUFFER_UNSUPPORTED; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
err::kFramebufferIncompleteWebGLDepthStencilInconsistant);
} }
if (mState.mWebGLDepthStencilAttachment.isAttached()) if (mState.mWebGLDepthStencilAttachment.isAttached())
...@@ -1352,24 +1449,32 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1352,24 +1449,32 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
if (mState.mWebGLDepthStencilAttachment.getDepthSize() == 0 || if (mState.mWebGLDepthStencilAttachment.getDepthSize() == 0 ||
mState.mWebGLDepthStencilAttachment.getStencilSize() == 0) mState.mWebGLDepthStencilAttachment.getStencilSize() == 0)
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentWebGLDepthStencilNoDepthOrStencilBits);
} }
if (!CheckMultiviewStateMatchesForCompleteness(firstAttachment, FramebufferStatus attachmentMultiviewCompleteness =
&mState.mWebGLDepthStencilAttachment)) CheckMultiviewStateMatchesForCompleteness(firstAttachment,
&mState.mWebGLDepthStencilAttachment);
if (!attachmentMultiviewCompleteness.isComplete())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR; return attachmentMultiviewCompleteness;
} }
} }
else if (mState.mStencilAttachment.isAttached() && else if (mState.mStencilAttachment.isAttached() &&
mState.mStencilAttachment.getDepthSize() > 0) mState.mStencilAttachment.getDepthSize() > 0)
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentWebGLStencilBufferHasDepthBits);
} }
else if (mState.mDepthAttachment.isAttached() && else if (mState.mDepthAttachment.isAttached() &&
mState.mDepthAttachment.getStencilSize() > 0) mState.mDepthAttachment.getStencilSize() > 0)
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
err::kFramebufferIncompleteAttachmentWebGLDepthBufferHasStencilBits);
} }
} }
...@@ -1380,7 +1485,8 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1380,7 +1485,8 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
GLint defaultHeight = mState.getDefaultHeight(); GLint defaultHeight = mState.getDefaultHeight();
if (!hasAttachments && (defaultWidth == 0 || defaultHeight == 0)) if (!hasAttachments && (defaultWidth == 0 || defaultHeight == 0))
{ {
return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; 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 2.0 and WebGL, all color attachments must have the same width and height.
...@@ -1388,14 +1494,18 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1388,14 +1494,18 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
if ((state.getClientMajorVersion() < 3 || state.getExtensions().webglCompatibility) && if ((state.getClientMajorVersion() < 3 || state.getExtensions().webglCompatibility) &&
!mState.attachmentsHaveSameDimensions()) !mState.attachmentsHaveSameDimensions())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; 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 // 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. // textures, the value of TEXTURE_FIXED_SAMPLE_LOCATIONS must be TRUE for all attached textures.
if (fixedSampleLocations.valid() && hasRenderbuffer && !fixedSampleLocations.value()) if (fixedSampleLocations.valid() && hasRenderbuffer && !fixedSampleLocations.value())
{ {
return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; return FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
err::kFramebufferIncompleteMultisampleNonFixedSamplesWithRenderbuffers);
} }
// The WebGL conformance tests implicitly define that all framebuffer // The WebGL conformance tests implicitly define that all framebuffer
...@@ -1405,11 +1515,12 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const ...@@ -1405,11 +1515,12 @@ GLenum Framebuffer::checkStatusWithGLFrontEnd(const Context *context) const
{ {
if (!mState.colorAttachmentsAreUniqueImages()) if (!mState.colorAttachmentsAreUniqueImages())
{ {
return GL_FRAMEBUFFER_UNSUPPORTED; return FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNSUPPORTED,
err::kFramebufferIncompleteAttachmentsNotUnique);
} }
} }
return GL_FRAMEBUFFER_COMPLETE; return FramebufferStatus::Complete();
} }
angle::Result Framebuffer::discard(const Context *context, size_t count, const GLenum *attachments) angle::Result Framebuffer::discard(const Context *context, size_t count, const GLenum *attachments)
...@@ -1607,7 +1718,7 @@ int Framebuffer::getSamples(const Context *context) const ...@@ -1607,7 +1718,7 @@ int Framebuffer::getSamples(const Context *context) const
return 0; return 0;
} }
ASSERT(mCachedStatus.valid() && mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE); ASSERT(mCachedStatus.valid() && mCachedStatus.value().isComplete());
// For a complete framebuffer, all attachments must have the same sample count. // For a complete framebuffer, all attachments must have the same sample count.
// In this case return the first nonzero sample size. // In this case return the first nonzero sample size.
...@@ -1624,7 +1735,7 @@ int Framebuffer::getReadBufferResourceSamples(const Context *context) const ...@@ -1624,7 +1735,7 @@ int Framebuffer::getReadBufferResourceSamples(const Context *context) const
return 0; return 0;
} }
ASSERT(mCachedStatus.valid() && mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE); ASSERT(mCachedStatus.valid() && mCachedStatus.value().isComplete());
const FramebufferAttachment *readAttachment = mState.getReadAttachment(); const FramebufferAttachment *readAttachment = mState.getReadAttachment();
ASSERT(readAttachment == nullptr || readAttachment->isAttached()); ASSERT(readAttachment == nullptr || readAttachment->isAttached());
......
...@@ -50,6 +50,17 @@ class State; ...@@ -50,6 +50,17 @@ class State;
class Texture; class Texture;
class TextureCapsMap; class TextureCapsMap;
struct FramebufferStatus
{
bool isComplete() const;
static FramebufferStatus Complete();
static FramebufferStatus Incomplete(GLenum status, const char *reason);
GLenum status = GL_FRAMEBUFFER_COMPLETE;
const char *reason = nullptr;
};
class FramebufferState final : angle::NonCopyable class FramebufferState final : angle::NonCopyable
{ {
public: public:
...@@ -293,7 +304,7 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -293,7 +304,7 @@ class Framebuffer final : public angle::ObserverInterface,
void invalidateCompletenessCache(); void invalidateCompletenessCache();
ANGLE_INLINE bool cachedStatusValid() { return mCachedStatus.valid(); } ANGLE_INLINE bool cachedStatusValid() { return mCachedStatus.valid(); }
ANGLE_INLINE GLenum checkStatus(const Context *context) const ANGLE_INLINE const FramebufferStatus &checkStatus(const Context *context) const
{ {
// The default framebuffer is always complete except when it is surfaceless in which // The default framebuffer is always complete except when it is surfaceless in which
// case it is always unsupported. // case it is always unsupported.
...@@ -309,7 +320,7 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -309,7 +320,7 @@ class Framebuffer final : public angle::ObserverInterface,
// Helper for checkStatus == GL_FRAMEBUFFER_COMPLETE. // Helper for checkStatus == GL_FRAMEBUFFER_COMPLETE.
ANGLE_INLINE bool isComplete(const Context *context) const ANGLE_INLINE bool isComplete(const Context *context) const
{ {
return (checkStatus(context) == GL_FRAMEBUFFER_COMPLETE); return checkStatus(context).isComplete();
} }
bool hasValidDepthStencil() const; bool hasValidDepthStencil() const;
...@@ -429,8 +440,8 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -429,8 +440,8 @@ class Framebuffer final : public angle::ObserverInterface,
FramebufferAttachment *attachment, FramebufferAttachment *attachment,
GLenum matchType, GLenum matchType,
GLuint matchId); GLuint matchId);
GLenum checkStatusWithGLFrontEnd(const Context *context) const; FramebufferStatus checkStatusWithGLFrontEnd(const Context *context) const;
GLenum checkStatusImpl(const Context *context) const; const FramebufferStatus &checkStatusImpl(const Context *context) const;
void setAttachment(const Context *context, void setAttachment(const Context *context,
GLenum type, GLenum type,
GLenum binding, GLenum binding,
...@@ -491,7 +502,7 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -491,7 +502,7 @@ class Framebuffer final : public angle::ObserverInterface,
FramebufferState mState; FramebufferState mState;
rx::FramebufferImpl *mImpl; rx::FramebufferImpl *mImpl;
mutable Optional<GLenum> mCachedStatus; mutable Optional<FramebufferStatus> mCachedStatus;
std::vector<angle::ObserverBinding> mDirtyColorAttachmentBindings; std::vector<angle::ObserverBinding> mDirtyColorAttachmentBindings;
angle::ObserverBinding mDirtyDepthAttachmentBinding; angle::ObserverBinding mDirtyDepthAttachmentBinding;
angle::ObserverBinding mDirtyStencilAttachmentBinding; angle::ObserverBinding mDirtyStencilAttachmentBinding;
......
...@@ -82,7 +82,7 @@ class FramebufferImpl : angle::NonCopyable ...@@ -82,7 +82,7 @@ class FramebufferImpl : angle::NonCopyable
GLbitfield mask, GLbitfield mask,
GLenum filter) = 0; GLenum filter) = 0;
virtual bool checkStatus(const gl::Context *context) const = 0; virtual gl::FramebufferStatus checkStatus(const gl::Context *context) const = 0;
virtual angle::Result syncState(const gl::Context *context, virtual angle::Result syncState(const gl::Context *context,
GLenum binding, GLenum binding,
......
...@@ -52,7 +52,7 @@ class MockFramebufferImpl : public rx::FramebufferImpl ...@@ -52,7 +52,7 @@ class MockFramebufferImpl : public rx::FramebufferImpl
GLbitfield, GLbitfield,
GLenum)); GLenum));
MOCK_CONST_METHOD1(checkStatus, bool(const gl::Context *)); MOCK_CONST_METHOD1(checkStatus, gl::FramebufferStatus(const gl::Context *));
MOCK_METHOD4(syncState, MOCK_METHOD4(syncState,
angle::Result(const gl::Context *, angle::Result(const gl::Context *,
...@@ -68,7 +68,8 @@ inline ::testing::NiceMock<MockFramebufferImpl> *MakeFramebufferMock() ...@@ -68,7 +68,8 @@ inline ::testing::NiceMock<MockFramebufferImpl> *MakeFramebufferMock()
::testing::NiceMock<MockFramebufferImpl> *framebufferImpl = ::testing::NiceMock<MockFramebufferImpl> *framebufferImpl =
new ::testing::NiceMock<MockFramebufferImpl>(); new ::testing::NiceMock<MockFramebufferImpl>();
// TODO(jmadill): add ON_CALLS for other returning methods // TODO(jmadill): add ON_CALLS for other returning methods
ON_CALL(*framebufferImpl, checkStatus(testing::_)).WillByDefault(::testing::Return(true)); ON_CALL(*framebufferImpl, checkStatus(testing::_))
.WillByDefault(::testing::Return(gl::FramebufferStatus::Complete()));
// We must mock the destructor since NiceMock doesn't work for destructors. // We must mock the destructor since NiceMock doesn't work for destructors.
EXPECT_CALL(*framebufferImpl, destructor()).Times(1).RetiresOnSaturation(); EXPECT_CALL(*framebufferImpl, destructor()).Times(1).RetiresOnSaturation();
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "common/bitset_utils.h" #include "common/bitset_utils.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/Framebuffer.h" #include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
...@@ -249,13 +250,15 @@ angle::Result FramebufferD3D::blit(const gl::Context *context, ...@@ -249,13 +250,15 @@ angle::Result FramebufferD3D::blit(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
bool FramebufferD3D::checkStatus(const gl::Context *context) const gl::FramebufferStatus FramebufferD3D::checkStatus(const gl::Context *context) const
{ {
// if we have both a depth and stencil buffer, they must refer to the same object // 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 // since we only support packed_depth_stencil and not separate depth and stencil
if (mState.hasSeparateDepthAndStencilAttachments()) if (mState.hasSeparateDepthAndStencilAttachments())
{ {
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
} }
// D3D11 does not allow for overlapping RenderTargetViews. // D3D11 does not allow for overlapping RenderTargetViews.
...@@ -266,17 +269,21 @@ bool FramebufferD3D::checkStatus(const gl::Context *context) const ...@@ -266,17 +269,21 @@ bool FramebufferD3D::checkStatus(const gl::Context *context) const
{ {
if (!mState.colorAttachmentsAreUniqueImages()) if (!mState.colorAttachmentsAreUniqueImages())
{ {
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedNonUniqueAttachments);
} }
} }
// D3D requires all render targets to have the same dimensions. // D3D requires all render targets to have the same dimensions.
if (!mState.attachmentsHaveSameDimensions()) if (!mState.attachmentsHaveSameDimensions())
{ {
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedMissmatchedDimensions);
} }
return true; return gl::FramebufferStatus::Complete();
} }
angle::Result FramebufferD3D::syncState(const gl::Context *context, angle::Result FramebufferD3D::syncState(const gl::Context *context,
......
...@@ -92,7 +92,7 @@ class FramebufferD3D : public FramebufferImpl ...@@ -92,7 +92,7 @@ class FramebufferD3D : public FramebufferImpl
GLbitfield mask, GLbitfield mask,
GLenum filter) override; GLenum filter) override;
bool checkStatus(const gl::Context *context) const override; gl::FramebufferStatus checkStatus(const gl::Context *context) const override;
angle::Result syncState(const gl::Context *context, angle::Result syncState(const gl::Context *context,
GLenum binding, GLenum binding,
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "common/bitset_utils.h" #include "common/bitset_utils.h"
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/State.h" #include "libANGLE/State.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
...@@ -1188,7 +1189,7 @@ bool FramebufferGL::shouldSyncStateBeforeCheckStatus() const ...@@ -1188,7 +1189,7 @@ bool FramebufferGL::shouldSyncStateBeforeCheckStatus() const
return true; return true;
} }
bool FramebufferGL::checkStatus(const gl::Context *context) const gl::FramebufferStatus FramebufferGL::checkStatus(const gl::Context *context) const
{ {
const FunctionsGL *functions = GetFunctionsGL(context); const FunctionsGL *functions = GetFunctionsGL(context);
StateManagerGL *stateManager = GetStateManagerGL(context); StateManagerGL *stateManager = GetStateManagerGL(context);
...@@ -1197,9 +1198,12 @@ bool FramebufferGL::checkStatus(const gl::Context *context) const ...@@ -1197,9 +1198,12 @@ bool FramebufferGL::checkStatus(const gl::Context *context) const
GLenum status = functions->checkFramebufferStatus(GL_FRAMEBUFFER); GLenum status = functions->checkFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) if (status != GL_FRAMEBUFFER_COMPLETE)
{ {
WARN() << "GL framebuffer returned incomplete."; WARN() << "GL framebuffer returned incomplete: " << gl::FmtHex(status);
return gl::FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteDriverUnsupported);
} }
return (status == GL_FRAMEBUFFER_COMPLETE);
return gl::FramebufferStatus::Complete();
} }
angle::Result FramebufferGL::syncState(const gl::Context *context, angle::Result FramebufferGL::syncState(const gl::Context *context,
......
...@@ -79,7 +79,7 @@ class FramebufferGL : public FramebufferImpl ...@@ -79,7 +79,7 @@ class FramebufferGL : public FramebufferImpl
// The GL back-end requires a full sync state before we call checkStatus. // The GL back-end requires a full sync state before we call checkStatus.
bool shouldSyncStateBeforeCheckStatus() const override; bool shouldSyncStateBeforeCheckStatus() const override;
bool checkStatus(const gl::Context *context) const override; gl::FramebufferStatus checkStatus(const gl::Context *context) const override;
angle::Result syncState(const gl::Context *context, angle::Result syncState(const gl::Context *context,
GLenum binding, GLenum binding,
......
...@@ -78,7 +78,7 @@ class FramebufferMtl : public FramebufferImpl ...@@ -78,7 +78,7 @@ class FramebufferMtl : public FramebufferImpl
GLbitfield mask, GLbitfield mask,
GLenum filter) override; GLenum filter) override;
bool checkStatus(const gl::Context *context) const override; gl::FramebufferStatus checkStatus(const gl::Context *context) const override;
angle::Result syncState(const gl::Context *context, angle::Result syncState(const gl::Context *context,
GLenum binding, GLenum binding,
...@@ -120,7 +120,7 @@ class FramebufferMtl : public FramebufferImpl ...@@ -120,7 +120,7 @@ class FramebufferMtl : public FramebufferImpl
private: private:
void reset(); void reset();
bool checkPackedDepthStencilAttachment() const; gl::FramebufferStatus checkPackedDepthStencilAttachment() const;
angle::Result invalidateImpl(ContextMtl *contextMtl, size_t count, const GLenum *attachments); angle::Result invalidateImpl(ContextMtl *contextMtl, size_t count, const GLenum *attachments);
angle::Result blitWithDraw(const gl::Context *context, angle::Result blitWithDraw(const gl::Context *context,
FramebufferMtl *srcFrameBuffer, FramebufferMtl *srcFrameBuffer,
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "common/MemoryBuffer.h" #include "common/MemoryBuffer.h"
#include "common/angleutils.h" #include "common/angleutils.h"
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/renderer/metal/BufferMtl.h" #include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/FrameBufferMtl.h" #include "libANGLE/renderer/metal/FrameBufferMtl.h"
...@@ -468,18 +469,22 @@ angle::Result FramebufferMtl::blitWithDraw(const gl::Context *context, ...@@ -468,18 +469,22 @@ angle::Result FramebufferMtl::blitWithDraw(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
bool FramebufferMtl::checkStatus(const gl::Context *context) const gl::FramebufferStatus FramebufferMtl::checkStatus(const gl::Context *context) const
{ {
if (!mState.attachmentsHaveSameDimensions()) if (!mState.attachmentsHaveSameDimensions())
{ {
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedMissmatchedDimensions);
} }
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
if (!contextMtl->getDisplay()->getFeatures().allowSeparatedDepthStencilBuffers.enabled && if (!contextMtl->getDisplay()->getFeatures().allowSeparatedDepthStencilBuffers.enabled &&
mState.hasSeparateDepthAndStencilAttachments()) mState.hasSeparateDepthAndStencilAttachments())
{ {
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
} }
if (mState.getDepthAttachment() && mState.getDepthAttachment()->getFormat().info->depthBits && if (mState.getDepthAttachment() && mState.getDepthAttachment()->getFormat().info->depthBits &&
...@@ -495,10 +500,10 @@ bool FramebufferMtl::checkStatus(const gl::Context *context) const ...@@ -495,10 +500,10 @@ bool FramebufferMtl::checkStatus(const gl::Context *context) const
return checkPackedDepthStencilAttachment(); return checkPackedDepthStencilAttachment();
} }
return true; return gl::FramebufferStatus::Complete();
} }
bool FramebufferMtl::checkPackedDepthStencilAttachment() const gl::FramebufferStatus FramebufferMtl::checkPackedDepthStencilAttachment() const
{ {
if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.0, 12.0)) if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.0, 12.0))
{ {
...@@ -509,7 +514,9 @@ bool FramebufferMtl::checkPackedDepthStencilAttachment() const ...@@ -509,7 +514,9 @@ bool FramebufferMtl::checkPackedDepthStencilAttachment() const
{ {
WARN() << "Packed depth stencil texture/buffer must not be mixed with other " WARN() << "Packed depth stencil texture/buffer must not be mixed with other "
"texture/buffer."; "texture/buffer.";
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
} }
} }
else else
...@@ -520,10 +527,12 @@ bool FramebufferMtl::checkPackedDepthStencilAttachment() const ...@@ -520,10 +527,12 @@ bool FramebufferMtl::checkPackedDepthStencilAttachment() const
{ {
WARN() << "Packed depth stencil texture/buffer must be bound to both depth & stencil " WARN() << "Packed depth stencil texture/buffer must be bound to both depth & stencil "
"attachment point."; "attachment point.";
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
} }
} }
return true; return gl::FramebufferStatus::Complete();
} }
angle::Result FramebufferMtl::syncState(const gl::Context *context, angle::Result FramebufferMtl::syncState(const gl::Context *context,
......
...@@ -151,9 +151,9 @@ angle::Result FramebufferNULL::blit(const gl::Context *context, ...@@ -151,9 +151,9 @@ angle::Result FramebufferNULL::blit(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
bool FramebufferNULL::checkStatus(const gl::Context *context) const gl::FramebufferStatus FramebufferNULL::checkStatus(const gl::Context *context) const
{ {
return true; return gl::FramebufferStatus::Complete();
} }
angle::Result FramebufferNULL::syncState(const gl::Context *context, angle::Result FramebufferNULL::syncState(const gl::Context *context,
......
...@@ -65,7 +65,7 @@ class FramebufferNULL : public FramebufferImpl ...@@ -65,7 +65,7 @@ class FramebufferNULL : public FramebufferImpl
GLbitfield mask, GLbitfield mask,
GLenum filter) override; GLenum filter) override;
bool checkStatus(const gl::Context *context) const override; gl::FramebufferStatus checkStatus(const gl::Context *context) const override;
angle::Result syncState(const gl::Context *context, angle::Result syncState(const gl::Context *context,
GLenum binding, GLenum binding,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "common/vulkan/vk_headers.h" #include "common/vulkan/vk_headers.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Display.h" #include "libANGLE/Display.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/renderer/renderer_utils.h" #include "libANGLE/renderer/renderer_utils.h"
#include "libANGLE/renderer/vulkan/ContextVk.h" #include "libANGLE/renderer/vulkan/ContextVk.h"
...@@ -1458,16 +1459,18 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk, ...@@ -1458,16 +1459,18 @@ angle::Result FramebufferVk::resolveColorWithCommand(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
bool FramebufferVk::checkStatus(const gl::Context *context) const gl::FramebufferStatus FramebufferVk::checkStatus(const gl::Context *context) const
{ {
// if we have both a depth and stencil buffer, they must refer to the same object // 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 // since we only support packed_depth_stencil and not separate depth and stencil
if (mState.hasSeparateDepthAndStencilAttachments()) if (mState.hasSeparateDepthAndStencilAttachments())
{ {
return false; return gl::FramebufferStatus::Incomplete(
GL_FRAMEBUFFER_UNSUPPORTED,
gl::err::kFramebufferIncompleteUnsupportedSeparateDepthStencilBuffers);
} }
return true; return gl::FramebufferStatus::Complete();
} }
angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk, angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk,
......
...@@ -107,7 +107,7 @@ class FramebufferVk : public FramebufferImpl ...@@ -107,7 +107,7 @@ class FramebufferVk : public FramebufferImpl
GLbitfield mask, GLbitfield mask,
GLenum filter) override; GLenum filter) override;
bool checkStatus(const gl::Context *context) const override; gl::FramebufferStatus checkStatus(const gl::Context *context) const override;
angle::Result syncState(const gl::Context *context, angle::Result syncState(const gl::Context *context,
GLenum binding, GLenum binding,
......
...@@ -763,9 +763,11 @@ template <GLenum ErrorCode = GL_INVALID_FRAMEBUFFER_OPERATION> ...@@ -763,9 +763,11 @@ template <GLenum ErrorCode = GL_INVALID_FRAMEBUFFER_OPERATION>
ANGLE_INLINE bool ValidateFramebufferComplete(const Context *context, ANGLE_INLINE bool ValidateFramebufferComplete(const Context *context,
const Framebuffer *framebuffer) const Framebuffer *framebuffer)
{ {
if (!framebuffer->isComplete(context)) const FramebufferStatus &framebufferStatus = framebuffer->checkStatus(context);
if (!framebufferStatus.isComplete())
{ {
context->validationError(ErrorCode, err::kFramebufferIncomplete); ASSERT(framebufferStatus.reason != nullptr);
context->validationError(ErrorCode, framebufferStatus.reason);
return false; return false;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment