Cache Framebuffer completeness.

Improves performance on the render-to-texture microbenchmark
by ~3x on the OpenGL back-end. Wipes out several of the top profling
hotspots on that benchmark.

BUG=angleproject:1388

Change-Id: I6a35a0b435b2ed3c83d32acdb9df090df98214ad
Reviewed-on: https://chromium-review.googlesource.com/348957
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Jamie Madill 2016-06-16 14:46:59 -04:00 коммит произвёл Commit Bot
Родитель 73d417edc0
Коммит 362876b157
12 изменённых файлов: 425 добавлений и 282 удалений

Просмотреть файл

@ -24,22 +24,21 @@
#include "libANGLE/renderer/RenderbufferImpl.h"
#include "libANGLE/renderer/SurfaceImpl.h"
using namespace angle;
namespace gl
{
namespace
{
void DetachMatchingAttachment(FramebufferAttachment *attachment, GLenum matchType, GLuint matchId)
void BindResourceChannel(ChannelBinding *binding, FramebufferAttachmentObject *resource)
{
if (attachment->isAttached() &&
attachment->type() == matchType &&
attachment->id() == matchId)
{
attachment->detach();
}
}
binding->bind(resource ? resource->getDirtyChannel() : nullptr);
}
} // anonymous namespace
FramebufferState::FramebufferState()
: mLabel(),
mColorAttachments(1),
@ -171,16 +170,35 @@ bool FramebufferState::attachmentsHaveSameDimensions() const
}
Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, GLuint id)
: mState(caps), mImpl(factory->createFramebuffer(mState)), mId(id)
: mState(caps),
mImpl(factory->createFramebuffer(mState)),
mId(id),
mCachedStatus(),
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{
ASSERT(mId != 0);
ASSERT(mImpl != nullptr);
ASSERT(mState.mColorAttachments.size() == static_cast<size_t>(caps.maxColorAttachments));
for (size_t colorIndex = 0; colorIndex < mState.mColorAttachments.size(); ++colorIndex)
{
mDirtyColorAttachmentBindings.push_back(ChannelBinding(
this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex)));
}
}
Framebuffer::Framebuffer(rx::SurfaceImpl *surface)
: mState(), mImpl(surface->createDefaultFramebuffer(mState)), mId(0)
: mState(),
mImpl(surface->createDefaultFramebuffer(mState)),
mId(0),
mCachedStatus(GL_FRAMEBUFFER_COMPLETE),
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{
ASSERT(mImpl != nullptr);
mDirtyColorAttachmentBindings.push_back(
ChannelBinding(this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0)));
}
Framebuffer::~Framebuffer()
@ -210,13 +228,28 @@ void Framebuffer::detachRenderbuffer(GLuint renderbufferId)
void Framebuffer::detachResourceById(GLenum resourceType, GLuint resourceId)
{
for (auto &colorAttachment : mState.mColorAttachments)
for (size_t colorIndex = 0; colorIndex < mState.mColorAttachments.size(); ++colorIndex)
{
DetachMatchingAttachment(&colorAttachment, resourceType, resourceId);
detachMatchingAttachment(&mState.mColorAttachments[colorIndex], resourceType, resourceId,
DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
}
DetachMatchingAttachment(&mState.mDepthAttachment, resourceType, resourceId);
DetachMatchingAttachment(&mState.mStencilAttachment, resourceType, resourceId);
detachMatchingAttachment(&mState.mDepthAttachment, resourceType, resourceId,
DIRTY_BIT_DEPTH_ATTACHMENT);
detachMatchingAttachment(&mState.mStencilAttachment, resourceType, resourceId,
DIRTY_BIT_STENCIL_ATTACHMENT);
}
void Framebuffer::detachMatchingAttachment(FramebufferAttachment *attachment,
GLenum matchType,
GLuint matchId,
size_t dirtyBit)
{
if (attachment->isAttached() && attachment->type() == matchType && attachment->id() == matchId)
{
attachment->detach();
mDirtyBits.set(dirtyBit);
}
}
const FramebufferAttachment *Framebuffer::getColorbuffer(size_t colorAttachment) const
@ -397,6 +430,18 @@ GLenum Framebuffer::checkStatus(const ContextState &state)
return GL_FRAMEBUFFER_COMPLETE;
}
if (hasAnyDirtyBit() || !mCachedStatus.valid())
{
mCachedStatus = checkStatusImpl(state);
}
return mCachedStatus.value();
}
GLenum Framebuffer::checkStatusImpl(const ContextState &state)
{
ASSERT(mId != 0);
unsigned int colorbufferSize = 0;
int samples = -1;
bool missingAttachment = true;
@ -644,7 +689,7 @@ Error Framebuffer::clear(rx::ContextImpl *context, GLbitfield mask)
{
if (context->getGLState().isRasterizerDiscardEnabled())
{
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
return mImpl->clear(context, mask);
@ -657,7 +702,7 @@ Error Framebuffer::clearBufferfv(rx::ContextImpl *context,
{
if (context->getGLState().isRasterizerDiscardEnabled())
{
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
return mImpl->clearBufferfv(context, buffer, drawbuffer, values);
@ -670,7 +715,7 @@ Error Framebuffer::clearBufferuiv(rx::ContextImpl *context,
{
if (context->getGLState().isRasterizerDiscardEnabled())
{
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
return mImpl->clearBufferuiv(context, buffer, drawbuffer, values);
@ -683,7 +728,7 @@ Error Framebuffer::clearBufferiv(rx::ContextImpl *context,
{
if (context->getGLState().isRasterizerDiscardEnabled())
{
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
return mImpl->clearBufferiv(context, buffer, drawbuffer, values);
@ -697,7 +742,7 @@ Error Framebuffer::clearBufferfi(rx::ContextImpl *context,
{
if (context->getGLState().isRasterizerDiscardEnabled())
{
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
return mImpl->clearBufferfi(context, buffer, drawbuffer, depth, stencil);
@ -719,11 +764,7 @@ Error Framebuffer::readPixels(rx::ContextImpl *context,
GLenum type,
GLvoid *pixels) const
{
Error error = mImpl->readPixels(context, area, format, type, pixels);
if (error.isError())
{
return error;
}
ANGLE_TRY(mImpl->readPixels(context, area, format, type, pixels));
Buffer *unpackBuffer = context->getGLState().getUnpackState().pixelBuffer.get();
if (unpackBuffer)
@ -731,7 +772,7 @@ Error Framebuffer::readPixels(rx::ContextImpl *context,
unpackBuffer->onPixelUnpack();
}
return Error(GL_NO_ERROR);
return NoError();
}
Error Framebuffer::blit(rx::ContextImpl *context,
@ -745,16 +786,15 @@ Error Framebuffer::blit(rx::ContextImpl *context,
int Framebuffer::getSamples(const ContextState &state)
{
if (checkStatus(state) == GL_FRAMEBUFFER_COMPLETE)
if (complete(state))
{
// for a complete framebuffer, all attachments must have the same sample count
// in this case return the first nonzero sample size
for (const FramebufferAttachment &colorAttachment : mState.mColorAttachments)
// For a complete framebuffer, all attachments must have the same sample count.
// In this case return the first nonzero sample size.
const auto *firstColorAttachment = mState.getFirstColorAttachment();
if (firstColorAttachment)
{
if (colorAttachment.isAttached())
{
return colorAttachment.getSamples();
}
ASSERT(firstColorAttachment->isAttached());
return firstColorAttachment->getSamples();
}
}
@ -791,6 +831,8 @@ void Framebuffer::setAttachment(GLenum type,
mState.mStencilAttachment.attach(type, binding, textureIndex, attachmentObj);
mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
BindResourceChannel(&mDirtyDepthAttachmentBinding, resource);
BindResourceChannel(&mDirtyStencilAttachmentBinding, resource);
}
else
{
@ -800,22 +842,26 @@ void Framebuffer::setAttachment(GLenum type,
case GL_DEPTH_ATTACHMENT:
mState.mDepthAttachment.attach(type, binding, textureIndex, resource);
mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
break;
BindResourceChannel(&mDirtyDepthAttachmentBinding, resource);
break;
case GL_STENCIL:
case GL_STENCIL_ATTACHMENT:
mState.mStencilAttachment.attach(type, binding, textureIndex, resource);
mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
break;
BindResourceChannel(&mDirtyStencilAttachmentBinding, resource);
break;
case GL_BACK:
mState.mColorAttachments[0].attach(type, binding, textureIndex, resource);
mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0);
break;
// No need for a resource binding for the default FBO, it's always complete.
break;
default:
{
size_t colorIndex = binding - GL_COLOR_ATTACHMENT0;
ASSERT(colorIndex < mState.mColorAttachments.size());
mState.mColorAttachments[colorIndex].attach(type, binding, textureIndex, resource);
mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
BindResourceChannel(&mDirtyColorAttachmentBindings[colorIndex], resource);
}
break;
}
@ -827,27 +873,25 @@ void Framebuffer::resetAttachment(GLenum binding)
setAttachment(GL_NONE, binding, ImageIndex::MakeInvalid(), nullptr);
}
void Framebuffer::syncState() const
void Framebuffer::syncState()
{
if (mDirtyBits.any())
{
mImpl->syncState(mDirtyBits);
mDirtyBits.reset();
mCachedStatus.reset();
}
}
int Framebuffer::getCachedSamples(const ContextState &state) const
void Framebuffer::signal(SignalToken token)
{
// TODO(jmadill): Framebuffer samples caching.
ASSERT(mDirtyBits.none());
return const_cast<Framebuffer *>(this)->getSamples(state);
// TOOD(jmadill): Make this only update individual attachments to do less work.
mCachedStatus.reset();
}
GLenum Framebuffer::getCachedStatus(const ContextState &state) const
bool Framebuffer::complete(const ContextState &state)
{
// TODO(jmadill): Framebuffer status caching.
ASSERT(mDirtyBits.none());
return const_cast<Framebuffer *>(this)->checkStatus(state);
return (checkStatus(state) == GL_FRAMEBUFFER_COMPLETE);
}
} // namespace gl

Просмотреть файл

@ -12,12 +12,14 @@
#include <vector>
#include "common/Optional.h"
#include "common/angleutils.h"
#include "libANGLE/Constants.h"
#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/RefCountObject.h"
#include "libANGLE/signal_utils.h"
namespace rx
{
@ -86,7 +88,7 @@ class FramebufferState final : angle::NonCopyable
GLenum mReadBufferState;
};
class Framebuffer final : public LabeledObject
class Framebuffer final : public LabeledObject, public angle::SignalReceiver
{
public:
Framebuffer(const Caps &caps, rx::GLImplFactory *factory, GLuint id);
@ -140,10 +142,8 @@ class Framebuffer final : public LabeledObject
int getSamples(const ContextState &state);
GLenum checkStatus(const ContextState &state);
// These methods do not change any state.
// TODO(jmadill): Remove ContextState parameter when able.
int getCachedSamples(const ContextState &state) const;
GLenum getCachedStatus(const ContextState &state) const;
// Helper for checkStatus == GL_FRAMEBUFFER_COMPLETE.
bool complete(const ContextState &state);
bool hasValidDepthStencil() const;
@ -200,19 +200,31 @@ class Framebuffer final : public LabeledObject
typedef std::bitset<DIRTY_BIT_MAX> DirtyBits;
bool hasAnyDirtyBit() const { return mDirtyBits.any(); }
void syncState() const;
void syncState();
protected:
// angle::SignalReceiver implementation
void signal(angle::SignalToken token) override;
private:
void detachResourceById(GLenum resourceType, GLuint resourceId);
void detachMatchingAttachment(FramebufferAttachment *attachment,
GLenum matchType,
GLuint matchId,
size_t dirtyBit);
GLenum checkStatusImpl(const ContextState &state);
FramebufferState mState;
rx::FramebufferImpl *mImpl;
GLuint mId;
// TODO(jmadill): See if we can make this non-mutable.
mutable DirtyBits mDirtyBits;
Optional<GLenum> mCachedStatus;
std::vector<angle::ChannelBinding> mDirtyColorAttachmentBindings;
angle::ChannelBinding mDirtyDepthAttachmentBinding;
angle::ChannelBinding mDirtyStencilAttachmentBinding;
DirtyBits mDirtyBits;
};
}
} // namespace gl
#endif // LIBANGLE_FRAMEBUFFER_H_

Просмотреть файл

@ -233,4 +233,9 @@ Error FramebufferAttachmentObject::getAttachmentRenderTarget(
return getAttachmentImpl()->getAttachmentRenderTarget(target, rtOut);
}
angle::BroadcastChannel *FramebufferAttachmentObject::getDirtyChannel()
{
return &mDirtyChannel;
}
} // namespace gl

Просмотреть файл

@ -15,6 +15,7 @@
#include "libANGLE/angletypes.h"
#include "libANGLE/Error.h"
#include "libANGLE/ImageIndex.h"
#include "libANGLE/signal_utils.h"
namespace egl
{
@ -165,8 +166,12 @@ class FramebufferAttachmentObject
Error getAttachmentRenderTarget(const FramebufferAttachment::Target &target,
rx::FramebufferAttachmentRenderTarget **rtOut) const;
angle::BroadcastChannel *getDirtyChannel();
protected:
virtual rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const = 0;
angle::BroadcastChannel mDirtyChannel;
};
inline Extents FramebufferAttachment::getSize() const

Просмотреть файл

@ -49,47 +49,39 @@ Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t heigh
{
orphanImages();
Error error = mRenderbuffer->setStorage(internalformat, width, height);
if (error.isError())
{
return error;
}
ANGLE_TRY(mRenderbuffer->setStorage(internalformat, width, height));
mWidth = static_cast<GLsizei>(width);
mHeight = static_cast<GLsizei>(height);
mInternalFormat = internalformat;
mSamples = 0;
return Error(GL_NO_ERROR);
mDirtyChannel.signal();
return NoError();
}
Error Renderbuffer::setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height)
{
orphanImages();
Error error = mRenderbuffer->setStorageMultisample(samples, internalformat, width, height);
if (error.isError())
{
return error;
}
ANGLE_TRY(mRenderbuffer->setStorageMultisample(samples, internalformat, width, height));
mWidth = static_cast<GLsizei>(width);
mHeight = static_cast<GLsizei>(height);
mInternalFormat = internalformat;
mSamples = static_cast<GLsizei>(samples);
return Error(GL_NO_ERROR);
mDirtyChannel.signal();
return NoError();
}
Error Renderbuffer::setStorageEGLImageTarget(egl::Image *image)
{
orphanImages();
Error error = mRenderbuffer->setStorageEGLImageTarget(image);
if (error.isError())
{
return error;
}
ANGLE_TRY(mRenderbuffer->setStorageEGLImageTarget(image));
setTargetImage(image);
@ -98,7 +90,9 @@ Error Renderbuffer::setStorageEGLImageTarget(egl::Image *image)
mInternalFormat = image->getInternalFormat();
mSamples = 0;
return Error(GL_NO_ERROR);
mDirtyChannel.signal();
return NoError();
}
rx::RenderbufferImpl *Renderbuffer::getImplementation()
@ -181,4 +175,4 @@ Extents Renderbuffer::getAttachmentSize(const FramebufferAttachment::Target & /*
{
return Extents(mWidth, mHeight, 1);
}
}
} // namespace gl

Просмотреть файл

@ -779,17 +779,14 @@ Error Texture::setImage(const PixelUnpackState &unpackState,
releaseTexImageInternal();
orphanImages();
Error error =
mTexture->setImage(target, level, internalFormat, size, format, type, unpackState, pixels);
if (error.isError())
{
return error;
}
ANGLE_TRY(
mTexture->setImage(target, level, internalFormat, size, format, type, unpackState, pixels));
mState.setImageDesc(target, level,
ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
mDirtyChannel.signal();
return Error(GL_NO_ERROR);
return NoError();
}
Error Texture::setSubImage(const PixelUnpackState &unpackState,
@ -820,17 +817,14 @@ Error Texture::setCompressedImage(const PixelUnpackState &unpackState,
releaseTexImageInternal();
orphanImages();
Error error = mTexture->setCompressedImage(target, level, internalFormat, size, unpackState,
imageSize, pixels);
if (error.isError())
{
return error;
}
ANGLE_TRY(mTexture->setCompressedImage(target, level, internalFormat, size, unpackState,
imageSize, pixels));
mState.setImageDesc(target, level,
ImageDesc(size, GetSizedInternalFormat(internalFormat, GL_UNSIGNED_BYTE)));
mDirtyChannel.signal();
return Error(GL_NO_ERROR);
return NoError();
}
Error Texture::setCompressedSubImage(const PixelUnpackState &unpackState,
@ -858,17 +852,14 @@ Error Texture::copyImage(GLenum target, size_t level, const Rectangle &sourceAre
releaseTexImageInternal();
orphanImages();
Error error = mTexture->copyImage(target, level, sourceArea, internalFormat, source);
if (error.isError())
{
return error;
}
ANGLE_TRY(mTexture->copyImage(target, level, sourceArea, internalFormat, source));
mState.setImageDesc(target, level,
ImageDesc(Extents(sourceArea.width, sourceArea.height, 1),
GetSizedInternalFormat(internalFormat, GL_UNSIGNED_BYTE)));
mDirtyChannel.signal();
return Error(GL_NO_ERROR);
return NoError();
}
Error Texture::copySubImage(GLenum target, size_t level, const Offset &destOffset, const Rectangle &sourceArea,
@ -888,17 +879,15 @@ Error Texture::setStorage(GLenum target, GLsizei levels, GLenum internalFormat,
releaseTexImageInternal();
orphanImages();
Error error = mTexture->setStorage(target, levels, internalFormat, size);
if (error.isError())
{
return error;
}
ANGLE_TRY(mTexture->setStorage(target, levels, internalFormat, size));
mState.mImmutableFormat = true;
mState.mImmutableLevels = static_cast<GLuint>(levels);
mState.clearImageDescs();
mState.setImageDescChain(0, static_cast<GLuint>(levels - 1), size, internalFormat);
return Error(GL_NO_ERROR);
mDirtyChannel.signal();
return NoError();
}
Error Texture::generateMipmap()
@ -926,6 +915,8 @@ Error Texture::generateMipmap()
baseImageInfo.internalFormat);
}
mDirtyChannel.signal();
return NoError();
}
@ -946,6 +937,7 @@ void Texture::bindTexImageFromSurface(egl::Surface *surface)
Extents size(surface->getWidth(), surface->getHeight(), 1);
ImageDesc desc(size, surface->getConfig()->renderTargetFormat);
mState.setImageDesc(mState.mTarget, 0, desc);
mDirtyChannel.signal();
}
void Texture::releaseTexImageFromSurface()
@ -957,6 +949,7 @@ void Texture::releaseTexImageFromSurface()
// Erase the image info for level 0
ASSERT(mState.mTarget == GL_TEXTURE_2D);
mState.clearImageDesc(mState.mTarget, 0);
mDirtyChannel.signal();
}
void Texture::bindStream(egl::Stream *stream)
@ -984,6 +977,7 @@ void Texture::acquireImageFromStream(const egl::Stream::GLTextureDescription &de
Extents size(desc.width, desc.height, 1);
mState.setImageDesc(mState.mTarget, 0, ImageDesc(size, desc.internalFormat));
mDirtyChannel.signal();
}
void Texture::releaseImageFromStream()
@ -993,6 +987,7 @@ void Texture::releaseImageFromStream()
// Set to incomplete
mState.clearImageDesc(mState.mTarget, 0);
mDirtyChannel.signal();
}
void Texture::releaseTexImageInternal()
@ -1016,11 +1011,7 @@ Error Texture::setEGLImageTarget(GLenum target, egl::Image *imageTarget)
releaseTexImageInternal();
orphanImages();
Error error = mTexture->setEGLImageTarget(target, imageTarget);
if (error.isError())
{
return error;
}
ANGLE_TRY(mTexture->setEGLImageTarget(target, imageTarget));
setTargetImage(imageTarget);
@ -1031,8 +1022,9 @@ Error Texture::setEGLImageTarget(GLenum target, egl::Image *imageTarget)
mState.clearImageDescs();
mState.setImageDesc(target, 0, ImageDesc(size, GetSizedInternalFormat(internalFormat, type)));
mDirtyChannel.signal();
return Error(GL_NO_ERROR);
return NoError();
}
Extents Texture::getAttachmentSize(const gl::FramebufferAttachment::Target &target) const
@ -1070,4 +1062,4 @@ rx::FramebufferAttachmentObjectImpl *Texture::getAttachmentImpl() const
{
return mTexture;
}
}
} // namespace gl

Просмотреть файл

@ -56,19 +56,14 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal
}
RenderTargetD3D *newRT = NULL;
gl::Error error =
mRenderer->createRenderTarget(static_cast<int>(width), static_cast<int>(height),
creationFormat, static_cast<GLsizei>(samples), &newRT);
if (error.isError())
{
return error;
}
ANGLE_TRY(mRenderer->createRenderTarget(static_cast<int>(width), static_cast<int>(height),
creationFormat, static_cast<GLsizei>(samples), &newRT));
SafeDelete(mRenderTarget);
mImage = nullptr;
mRenderTarget = newRT;
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
gl::Error RenderbufferD3D::setStorageEGLImageTarget(egl::Image *image)
@ -76,7 +71,7 @@ gl::Error RenderbufferD3D::setStorageEGLImageTarget(egl::Image *image)
mImage = GetImplAs<EGLImageD3D>(image);
SafeDelete(mRenderTarget);
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
gl::Error RenderbufferD3D::getRenderTarget(RenderTargetD3D **outRenderTarget)
@ -88,7 +83,7 @@ gl::Error RenderbufferD3D::getRenderTarget(RenderTargetD3D **outRenderTarget)
else
{
*outRenderTarget = mRenderTarget;
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
}

Просмотреть файл

@ -38,11 +38,7 @@ gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *attachme
TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture);
TextureStorage *texStorage = nullptr;
gl::Error error = textureD3D->getNativeTexture(&texStorage);
if (error.isError())
{
return error;
}
ANGLE_TRY(textureD3D->getNativeTexture(&texStorage));
if (texStorage)
{
@ -53,7 +49,7 @@ gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *attachme
}
}
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
void UpdateCachedRenderTarget(const gl::FramebufferAttachment *attachment,
@ -99,33 +95,19 @@ gl::Error Framebuffer11::invalidateSwizzles() const
{
if (colorAttachment.isAttached())
{
gl::Error error = InvalidateAttachmentSwizzles(&colorAttachment);
if (error.isError())
{
return error;
}
ANGLE_TRY(InvalidateAttachmentSwizzles(&colorAttachment));
}
}
gl::Error error = InvalidateAttachmentSwizzles(mState.getDepthAttachment());
if (error.isError())
{
return error;
}
ANGLE_TRY(InvalidateAttachmentSwizzles(mState.getDepthAttachment()));
ANGLE_TRY(InvalidateAttachmentSwizzles(mState.getStencilAttachment()));
error = InvalidateAttachmentSwizzles(mState.getStencilAttachment());
if (error.isError())
{
return error;
}
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
gl::Error Framebuffer11::clearImpl(ContextImpl *context, const ClearParameters &clearParams)
{
Clear11 *clearer = mRenderer->getClearer();
gl::Error error(GL_NO_ERROR);
const gl::FramebufferAttachment *colorAttachment = mState.getFirstColorAttachment();
if (clearParams.scissorEnabled == true && colorAttachment != nullptr &&
@ -139,25 +121,16 @@ gl::Error Framebuffer11::clearImpl(ContextImpl *context, const ClearParameters &
presentPathFastClearParams.scissor.y = framebufferSize.height -
presentPathFastClearParams.scissor.y -
presentPathFastClearParams.scissor.height;
error = clearer->clearFramebuffer(presentPathFastClearParams, mState);
ANGLE_TRY(clearer->clearFramebuffer(presentPathFastClearParams, mState));
}
else
{
error = clearer->clearFramebuffer(clearParams, mState);
ANGLE_TRY(clearer->clearFramebuffer(clearParams, mState));
}
if (error.isError())
{
return error;
}
ANGLE_TRY(invalidateSwizzles());
error = invalidateSwizzles();
if (error.isError())
{
return error;
}
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
gl::Error Framebuffer11::invalidate(size_t count, const GLenum *attachments)
@ -177,7 +150,7 @@ gl::Error Framebuffer11::invalidateBase(size_t count, const GLenum *attachments,
if (!deviceContext1)
{
// DiscardView() is only supported on ID3D11DeviceContext1
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
bool foundDepth = false;
@ -206,37 +179,10 @@ gl::Error Framebuffer11::invalidateBase(size_t count, const GLenum *attachments,
ASSERT((attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15) ||
(attachments[i] == GL_COLOR));
RenderTarget11 *renderTarget = nullptr;
ID3D11View *colorView = nullptr;
gl::Error error(GL_NO_ERROR);
size_t colorAttachmentID = 0;
if (attachments[i] == GL_COLOR)
{
colorAttachmentID = 0;
}
else
{
colorAttachmentID = attachments[i] - GL_COLOR_ATTACHMENT0;
}
if (mState.getColorAttachment(static_cast<unsigned int>(colorAttachmentID)))
{
error = mState.getColorAttachment(static_cast<unsigned int>(colorAttachmentID))
->getRenderTarget(&renderTarget);
if (error.isError())
{
return error;
}
colorView = renderTarget->getRenderTargetView();
if (colorView != nullptr)
{
deviceContext1->DiscardView(colorView);
}
}
size_t colorIndex =
(attachments[i] == GL_COLOR ? 0u : (attachments[i] - GL_COLOR_ATTACHMENT0));
auto colorAttachment = mState.getColorAttachment(colorIndex);
ANGLE_TRY(invalidateAttachment(colorAttachment));
break;
}
}
@ -268,51 +214,39 @@ gl::Error Framebuffer11::invalidateBase(size_t count, const GLenum *attachments,
if (discardDepth && mState.getDepthAttachment())
{
RenderTarget11 *renderTarget = nullptr;
ID3D11View *depthView = nullptr;
gl::Error error(GL_NO_ERROR);
error = mState.getDepthAttachment()->getRenderTarget(&renderTarget);
if (error.isError())
{
return error;
}
depthView = renderTarget->getDepthStencilView();
if (depthView != nullptr)
{
deviceContext1->DiscardView(depthView);
}
ANGLE_TRY(invalidateAttachment(mState.getDepthAttachment()));
}
if (discardStencil && mState.getStencilAttachment())
{
RenderTarget11 *renderTarget = nullptr;
ID3D11View *stencilView = nullptr;
gl::Error error(GL_NO_ERROR);
error = mState.getStencilAttachment()->getRenderTarget(&renderTarget);
if (error.isError())
{
return error;
}
stencilView = renderTarget->getDepthStencilView();
if (stencilView != nullptr)
{
deviceContext1->DiscardView(stencilView);
}
ANGLE_TRY(invalidateAttachment(mState.getStencilAttachment()));
}
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
gl::Error Framebuffer11::invalidateSub(size_t, const GLenum *, const gl::Rectangle &)
{
// A no-op implementation conforms to the spec, so don't call UNIMPLEMENTED()
return gl::Error(GL_NO_ERROR);
return gl::NoError();
}
gl::Error Framebuffer11::invalidateAttachment(const gl::FramebufferAttachment *attachment) const
{
ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported();
ASSERT(deviceContext1);
ASSERT(attachment && attachment->isAttached());
RenderTarget11 *renderTarget = nullptr;
ANGLE_TRY(attachment->getRenderTarget(&renderTarget));
ID3D11View *view = renderTarget->getRenderTargetView();
if (view != nullptr)
{
deviceContext1->DiscardView(view);
}
return gl::NoError();
}
gl::Error Framebuffer11::readPixelsImpl(const gl::Rectangle &area,
@ -361,11 +295,7 @@ gl::Error Framebuffer11::blitImpl(const gl::Rectangle &sourceArea,
ASSERT(readBuffer);
RenderTargetD3D *readRenderTarget = nullptr;
gl::Error error = readBuffer->getRenderTarget(&readRenderTarget);
if (error.isError())
{
return error;
}
ANGLE_TRY(readBuffer->getRenderTarget(&readRenderTarget));
ASSERT(readRenderTarget);
const auto &colorAttachments = mState.getColorAttachments();
@ -379,11 +309,7 @@ gl::Error Framebuffer11::blitImpl(const gl::Rectangle &sourceArea,
drawBufferStates[colorAttachment] != GL_NONE)
{
RenderTargetD3D *drawRenderTarget = nullptr;
error = drawBuffer.getRenderTarget(&drawRenderTarget);
if (error.isError())
{
return error;
}
ANGLE_TRY(drawBuffer.getRenderTarget(&drawRenderTarget));
ASSERT(drawRenderTarget);
const bool invertColorSource = UsePresentPathFast(mRenderer, readBuffer);
@ -404,13 +330,9 @@ gl::Error Framebuffer11::blitImpl(const gl::Rectangle &sourceArea,
actualDestArea.height = -destArea.height;
}
error = mRenderer->blitRenderbufferRect(actualSourceArea, actualDestArea,
readRenderTarget, drawRenderTarget, filter,
scissor, blitRenderTarget, false, false);
if (error.isError())
{
return error;
}
ANGLE_TRY(mRenderer->blitRenderbufferRect(
actualSourceArea, actualDestArea, readRenderTarget, drawRenderTarget, filter,
scissor, blitRenderTarget, false, false));
}
}
}
@ -421,39 +343,23 @@ gl::Error Framebuffer11::blitImpl(const gl::Rectangle &sourceArea,
ASSERT(readBuffer);
RenderTargetD3D *readRenderTarget = nullptr;
gl::Error error = readBuffer->getRenderTarget(&readRenderTarget);
if (error.isError())
{
return error;
}
ANGLE_TRY(readBuffer->getRenderTarget(&readRenderTarget));
ASSERT(readRenderTarget);
const gl::FramebufferAttachment *drawBuffer = mState.getDepthOrStencilAttachment();
ASSERT(drawBuffer);
RenderTargetD3D *drawRenderTarget = nullptr;
error = drawBuffer->getRenderTarget(&drawRenderTarget);
if (error.isError())
{
return error;
}
ANGLE_TRY(drawBuffer->getRenderTarget(&drawRenderTarget));
ASSERT(drawRenderTarget);
error = mRenderer->blitRenderbufferRect(sourceArea, destArea, readRenderTarget, drawRenderTarget, filter, scissor,
false, blitDepth, blitStencil);
if (error.isError())
{
return error;
}
ANGLE_TRY(mRenderer->blitRenderbufferRect(sourceArea, destArea, readRenderTarget,
drawRenderTarget, filter, scissor, false,
blitDepth, blitStencil));
}
gl::Error error = invalidateSwizzles();
if (error.isError())
{
return error;
}
return gl::Error(GL_NO_ERROR);
ANGLE_TRY(invalidateSwizzles());
return gl::NoError();
}
GLenum Framebuffer11::getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const

Просмотреть файл

@ -66,6 +66,7 @@ class Framebuffer11 : public FramebufferD3D, public angle::SignalReceiver
const gl::Framebuffer *sourceFramebuffer) override;
gl::Error invalidateBase(size_t count, const GLenum *attachments, bool useEXTBehavior) const;
gl::Error invalidateAttachment(const gl::FramebufferAttachment *attachment) const;
GLenum getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const override;

Просмотреть файл

@ -1498,16 +1498,14 @@ gl::Error Renderer11::updateState(const gl::ContextState &data, GLenum drawMode)
// Applies the render target surface, depth stencil surface, viewport rectangle and
// scissor rectangle to the renderer
gl::Framebuffer *framebufferObject = glState.getDrawFramebuffer();
ASSERT(framebufferObject &&
framebufferObject->getCachedStatus(data) == GL_FRAMEBUFFER_COMPLETE);
ANGLE_TRY(applyRenderTarget(framebufferObject));
gl::Framebuffer *framebuffer = glState.getDrawFramebuffer();
ASSERT(framebuffer && !framebuffer->hasAnyDirtyBit() && framebuffer->complete(data));
ANGLE_TRY(applyRenderTarget(framebuffer));
// Set the present path state
const bool presentPathFastActive =
UsePresentPathFast(this, framebufferObject->getFirstColorbuffer());
mStateManager.updatePresentPath(presentPathFastActive,
framebufferObject->getFirstColorbuffer());
auto firstColorAttachment = framebuffer->getFirstColorbuffer();
const bool presentPathFastActive = UsePresentPathFast(this, firstColorAttachment);
mStateManager.updatePresentPath(presentPathFastActive, firstColorAttachment);
// Setting viewport state
mStateManager.setViewport(&data.getCaps(), glState.getViewport(), glState.getNearPlane(),
@ -1517,7 +1515,7 @@ gl::Error Renderer11::updateState(const gl::ContextState &data, GLenum drawMode)
mStateManager.setScissorRectangle(glState.getScissor(), glState.isScissorTestEnabled());
// Applying rasterizer state to D3D11 device
int samples = framebufferObject->getCachedSamples(data);
int samples = framebuffer->getSamples(data);
gl::RasterizerState rasterizer = glState.getRasterizerState();
rasterizer.pointDrawMode = (drawMode == GL_POINTS);
rasterizer.multiSample = (samples != 0);
@ -1526,7 +1524,7 @@ gl::Error Renderer11::updateState(const gl::ContextState &data, GLenum drawMode)
// Setting blend state
unsigned int mask = GetBlendSampleMask(data, samples);
ANGLE_TRY(mStateManager.setBlendState(framebufferObject, glState.getBlendState(),
ANGLE_TRY(mStateManager.setBlendState(framebuffer, glState.getBlendState(),
glState.getBlendColor(), mask));
// Setting depth stencil state

Просмотреть файл

@ -893,11 +893,10 @@ gl::Error Renderer9::updateState(Context9 *context, GLenum drawMode)
// Applies the render target surface, depth stencil surface, viewport rectangle and
// scissor rectangle to the renderer
const gl::Framebuffer *framebufferObject = glState.getDrawFramebuffer();
ASSERT(framebufferObject &&
framebufferObject->getCachedStatus(data) == GL_FRAMEBUFFER_COMPLETE);
gl::Framebuffer *framebuffer = glState.getDrawFramebuffer();
ASSERT(framebuffer && !framebuffer->hasAnyDirtyBit() && framebuffer->complete(data));
ANGLE_TRY(applyRenderTarget(context, framebufferObject));
ANGLE_TRY(applyRenderTarget(context, framebuffer));
// Setting viewport state
setViewport(glState.getViewport(), glState.getNearPlane(), glState.getFarPlane(), drawMode,
@ -907,7 +906,7 @@ gl::Error Renderer9::updateState(Context9 *context, GLenum drawMode)
setScissorRectangle(glState.getScissor(), glState.isScissorTestEnabled());
// Setting blend, depth stencil, and rasterizer states
int samples = framebufferObject->getCachedSamples(data);
int samples = framebuffer->getSamples(data);
gl::RasterizerState rasterizer = glState.getRasterizerState();
rasterizer.pointDrawMode = (drawMode == GL_POINTS);
rasterizer.multiSample = (samples != 0);
@ -927,8 +926,10 @@ void Renderer9::setScissorRectangle(const gl::Rectangle &scissor, bool enabled)
gl::Error Renderer9::setBlendDepthRasterStates(const gl::ContextState &glData, GLenum drawMode)
{
const auto &glState = glData.getState();
int samples = glState.getDrawFramebuffer()->getCachedSamples(glData);
const auto &glState = glData.getState();
auto drawFramebuffer = glState.getDrawFramebuffer();
ASSERT(!drawFramebuffer->hasAnyDirtyBit());
int samples = drawFramebuffer->getSamples(glData);
gl::RasterizerState rasterizer = glState.getRasterizerState();
rasterizer.pointDrawMode = (drawMode == GL_POINTS);
rasterizer.multiSample = (samples != 0);

Просмотреть файл

@ -9,6 +9,7 @@
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
@ -18,7 +19,7 @@ namespace
class StateChangeTest : public ANGLETest
{
protected:
StateChangeTest() : mFramebuffer(0)
StateChangeTest()
{
setWindowWidth(64);
setWindowHeight(64);
@ -36,9 +37,8 @@ class StateChangeTest : public ANGLETest
ANGLETest::SetUp();
glGenFramebuffers(1, &mFramebuffer);
mTextures.resize(2, 0);
glGenTextures(2, mTextures.data());
glGenRenderbuffers(1, &mRenderbuffer);
ASSERT_GL_NO_ERROR();
}
@ -57,11 +57,14 @@ class StateChangeTest : public ANGLETest
mTextures.clear();
}
glDeleteRenderbuffers(1, &mRenderbuffer);
ANGLETest::TearDown();
}
GLuint mFramebuffer;
std::vector<GLuint> mTextures;
GLuint mFramebuffer = 0;
GLuint mRenderbuffer = 0;
std::vector<GLuint> mTextures = {0, 0};
};
class StateChangeTestES3 : public StateChangeTest
@ -141,6 +144,148 @@ TEST_P(StateChangeTest, CopyTexSubImage2DSync)
ASSERT_GL_NO_ERROR();
}
// Test that Framebuffer completeness caching works when color attachments change.
TEST_P(StateChangeTest, FramebufferIncompleteColorAttachment)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Change the texture at color attachment 0 to be non-color-renderable.
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 16, 16, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that caching works when color attachments change with TexStorage.
TEST_P(StateChangeTest, FramebufferIncompleteWithTexStorage)
{
if (!extensionEnabled("GL_EXT_texture_storage"))
{
std::cout << "Test skipped because TexStorage2DEXT not available." << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Change the texture at color attachment 0 to be non-color-renderable.
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_ALPHA8_EXT, 16, 16);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that caching works when color attachments change with CompressedTexImage2D.
TEST_P(StateChangeTestES3, FramebufferIncompleteWithCompressedTex)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Change the texture at color attachment 0 to be non-color-renderable.
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB8_ETC2, 16, 16, 0, 64, nullptr);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that caching works when color attachments are deleted.
TEST_P(StateChangeTestES3, FramebufferIncompleteWhenAttachmentDeleted)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Delete the texture at color attachment 0.
glDeleteTextures(1, &mTextures[0]);
mTextures[0] = 0;
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that Framebuffer completeness caching works when depth attachments change.
TEST_P(StateChangeTest, FramebufferIncompleteDepthAttachment)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 16, 16);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mRenderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Change the texture at color attachment 0 to be non-depth-renderable.
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that Framebuffer completeness caching works when stencil attachments change.
TEST_P(StateChangeTest, FramebufferIncompleteStencilAttachment)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, 16, 16);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
mRenderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Change the texture at the stencil attachment to be non-stencil-renderable.
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that Framebuffer completeness caching works when depth-stencil attachments change.
TEST_P(StateChangeTest, FramebufferIncompleteDepthStencilAttachment)
{
if (getClientVersion() < 3 && !extensionEnabled("GL_OES_packed_depth_stencil"))
{
std::cout << "Test skipped because packed depth+stencil not availble." << std::endl;
return;
}
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 16, 16);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
mRenderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Change the texture the depth-stencil attachment to be non-depth-stencil-renderable.
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Ensure that CopyTexSubImage3D syncs framebuffer changes.
TEST_P(StateChangeTestES3, CopyTexSubImage3DSync)
{
@ -240,6 +385,51 @@ TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync)
ASSERT_GL_NO_ERROR();
}
// Tests calling invalidate on incomplete framebuffers after switching attachments.
// Adapted partially from WebGL 2 test "renderbuffers/invalidate-framebuffer"
TEST_P(StateChangeTestES3, IncompleteRenderbufferAttachmentInvalidateSync)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
GLint samples = 0;
glGetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, 1, &samples);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
ASSERT_GL_NO_ERROR();
// invalidate the framebuffer when the attachment is incomplete: no storage allocated to the
// attached renderbuffer
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
GLenum attachments1[] = {GL_COLOR_ATTACHMENT0};
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
ASSERT_GL_NO_ERROR();
glRenderbufferStorageMultisample(GL_RENDERBUFFER, static_cast<GLsizei>(samples), GL_RGBA8,
getWindowWidth(), getWindowHeight());
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf.get());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
renderbuf.get());
ASSERT_GL_NO_ERROR();
// invalidate the framebuffer when the attachment is incomplete: no storage allocated to the
// attached renderbuffer
// Note: the bug will only repro *without* a call to checkStatus before the invalidate.
GLenum attachments2[] = {GL_DEPTH_ATTACHMENT};
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments2);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, static_cast<GLsizei>(samples),
GL_DEPTH_COMPONENT16, getWindowWidth(), getWindowHeight());
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClear(GL_DEPTH_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
}
class StateChangeRenderTest : public StateChangeTest
{
protected: