aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeoff Lang <geofflang@chromium.org>2024-04-11 16:37:27 -0400
committerAngle LUCI CQ <angle-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-04-29 17:14:18 +0000
commit44ce58871c69cc790c70aaa0fdf2cff58a9d6c49 (patch)
treeae354eb0944c0d40aea4da90b08297a8a86dc966
parenta0b721bc70739d0bf2136e0cfd81cb406b4da191 (diff)
downloadangle-44ce58871c69cc790c70aaa0fdf2cff58a9d6c49.tar.gz
Allow the backend to do resource init for framebuffers.
The frontend framebuffer would loop through attachments which needed to be initialized and call initializeContents on them individually. For the GL backend this is inefficient because each of these resources is bound to a scratch framebuffer and cleared when the entire original framebuffer could have been cleared at once. The frontend now accumulates a set of attachments that need to be cleared and sends it to the FramebufferImpl. The default FramebufferImpl does the old logic of calling initializeContents on each attachment. FramebufferGL has an optimized path to clear the whole framebuffer if possible. Bug: angleproject:8642 Change-Id: I574cd03307794a6c7b2666976784e4d4dca1d08c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5448552 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>
-rw-r--r--src/libANGLE/Framebuffer.cpp243
-rw-r--r--src/libANGLE/Framebuffer.h6
-rw-r--r--src/libANGLE/FramebufferAttachment.cpp2
-rw-r--r--src/libANGLE/FramebufferAttachment.h2
-rw-r--r--src/libANGLE/renderer/FramebufferImpl.cpp40
-rw-r--r--src/libANGLE/renderer/FramebufferImpl.h5
-rw-r--r--src/libANGLE/renderer/gl/BlitGL.cpp126
-rw-r--r--src/libANGLE/renderer/gl/BlitGL.h2
-rw-r--r--src/libANGLE/renderer/gl/FramebufferGL.cpp18
-rw-r--r--src/libANGLE/renderer/gl/FramebufferGL.h5
-rw-r--r--src/libANGLE/renderer/gl/SurfaceGL.cpp11
-rw-r--r--src/tests/gl_tests/RobustResourceInitTest.cpp59
12 files changed, 355 insertions, 164 deletions
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index 40829f00b7..a7a1cc0016 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -2505,7 +2505,9 @@ angle::Result Framebuffer::ensureClearAttachmentsInitialized(const Context *cont
// If the impl encounters an error during a a full (non-partial) clear, the attachments will
// still be marked initialized. This simplifies design, allowing this method to be called before
// the clear.
- markDrawAttachmentsInitialized(color, depth, stencil);
+ DrawBufferMask clearedColorAttachments =
+ color ? mState.getEnabledDrawBuffers() : DrawBufferMask();
+ markAttachmentsInitialized(clearedColorAttachments, depth, stencil);
return angle::Result::Continue;
}
@@ -2516,20 +2518,67 @@ angle::Result Framebuffer::ensureClearBufferAttachmentsInitialized(const Context
{
if (!context->isRobustResourceInitEnabled() ||
context->getState().isRasterizerDiscardEnabled() ||
- context->isClearBufferMaskedOut(buffer, drawbuffer))
+ context->isClearBufferMaskedOut(buffer, drawbuffer) || mState.mResourceNeedsInit.none())
{
return angle::Result::Continue;
}
- if (partialBufferClearNeedsInit(context, buffer))
+ DrawBufferMask clearColorAttachments;
+ bool clearDepth = false;
+ bool clearStencil = false;
+
+ switch (buffer)
{
- ANGLE_TRY(ensureBufferInitialized(context, buffer, drawbuffer));
+ case GL_COLOR:
+ {
+ ASSERT(drawbuffer < static_cast<GLint>(mState.mColorAttachments.size()));
+ if (mState.mResourceNeedsInit[drawbuffer])
+ {
+ clearColorAttachments.set(drawbuffer);
+ }
+ break;
+ }
+ case GL_DEPTH:
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
+ {
+ clearDepth = true;
+ }
+ break;
+ }
+ case GL_STENCIL:
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
+ {
+ clearStencil = true;
+ }
+ break;
+ }
+ case GL_DEPTH_STENCIL:
+ {
+ if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
+ {
+ clearDepth = true;
+ }
+ if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
+ {
+ clearStencil = true;
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
}
- // If the impl encounters an error during a a full (non-partial) clear, the attachments will
- // still be marked initialized. This simplifies design, allowing this method to be called before
- // the clear.
- markBufferInitialized(buffer, drawbuffer);
+ if (partialBufferClearNeedsInit(context, buffer) &&
+ (clearColorAttachments.any() || clearDepth || clearStencil))
+ {
+ ANGLE_TRY(mImpl->ensureAttachmentsInitialized(context, clearColorAttachments, clearDepth,
+ clearStencil));
+ }
+
+ markAttachmentsInitialized(clearColorAttachments, clearDepth, clearStencil);
return angle::Result::Continue;
}
@@ -2541,24 +2590,34 @@ angle::Result Framebuffer::ensureDrawAttachmentsInitialized(const Context *conte
return angle::Result::Continue;
}
+ DrawBufferMask clearColorAttachments;
+ bool clearDepth = false;
+ bool clearStencil = false;
+
// Note: we don't actually filter by the draw attachment enum. Just init everything.
for (size_t bit : mState.mResourceNeedsInit)
{
switch (bit)
{
case DIRTY_BIT_DEPTH_ATTACHMENT:
- ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
+ clearDepth = true;
break;
case DIRTY_BIT_STENCIL_ATTACHMENT:
- ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
+ clearStencil = true;
break;
default:
- ANGLE_TRY(InitAttachment(context, &mState.mColorAttachments[bit]));
+ clearColorAttachments[bit] = true;
break;
}
}
- mState.mResourceNeedsInit.reset();
+ if (clearColorAttachments.any() || clearDepth || clearStencil)
+ {
+ ANGLE_TRY(mImpl->ensureAttachmentsInitialized(context, clearColorAttachments, clearDepth,
+ clearStencil));
+ markAttachmentsInitialized(clearColorAttachments, clearDepth, clearStencil);
+ }
+
return angle::Result::Continue;
}
@@ -2571,6 +2630,10 @@ angle::Result Framebuffer::ensureReadAttachmentsInitialized(const Context *conte
return angle::Result::Continue;
}
+ DrawBufferMask clearColorAttachments;
+ bool clearDepth = false;
+ bool clearStencil = false;
+
if (mState.mReadBufferState != GL_NONE)
{
if (isDefault())
@@ -2586,47 +2649,42 @@ angle::Result Framebuffer::ensureReadAttachmentsInitialized(const Context *conte
size_t readIndex = mState.getReadIndex();
if (mState.mResourceNeedsInit[readIndex])
{
- ANGLE_TRY(InitAttachment(context, &mState.mColorAttachments[readIndex]));
- mState.mResourceNeedsInit.reset(readIndex);
+ clearColorAttachments[readIndex] = true;
}
}
}
// Conservatively init depth since it can be read by BlitFramebuffer.
- if (hasDepth())
+ if (hasDepth() && mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
{
- if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
- {
- ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
- mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
- }
+ clearDepth = true;
}
// Conservatively init stencil since it can be read by BlitFramebuffer.
- if (hasStencil())
+ if (hasStencil() && mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
{
- if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
- {
- ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
- mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
- }
+ clearStencil = true;
+ }
+
+ if (clearColorAttachments.any() || clearDepth || clearStencil)
+ {
+ ANGLE_TRY(mImpl->ensureAttachmentsInitialized(context, clearColorAttachments, clearDepth,
+ clearStencil));
+ markAttachmentsInitialized(clearColorAttachments, clearDepth, clearStencil);
}
return angle::Result::Continue;
}
-void Framebuffer::markDrawAttachmentsInitialized(bool color, bool depth, bool stencil)
+void Framebuffer::markAttachmentsInitialized(const DrawBufferMask &color, bool depth, bool stencil)
{
// Mark attachments as initialized.
- if (color)
+ for (auto colorIndex : color)
{
- for (auto colorIndex : mState.mEnabledDrawBuffers)
- {
- auto &colorAttachment = mState.mColorAttachments[colorIndex];
- ASSERT(colorAttachment.isAttached());
- colorAttachment.setInitState(InitState::Initialized);
- mState.mResourceNeedsInit.reset(colorIndex);
- }
+ auto &colorAttachment = mState.mColorAttachments[colorIndex];
+ ASSERT(colorAttachment.isAttached());
+ colorAttachment.setInitState(InitState::Initialized);
+ mState.mResourceNeedsInit.reset(colorIndex);
}
if (depth && mState.mDepthAttachment.isAttached())
@@ -2642,58 +2700,6 @@ void Framebuffer::markDrawAttachmentsInitialized(bool color, bool depth, bool st
}
}
-void Framebuffer::markBufferInitialized(GLenum bufferType, GLint bufferIndex)
-{
- switch (bufferType)
- {
- case GL_COLOR:
- {
- ASSERT(bufferIndex < static_cast<GLint>(mState.mColorAttachments.size()));
- if (mState.mColorAttachments[bufferIndex].isAttached())
- {
- mState.mColorAttachments[bufferIndex].setInitState(InitState::Initialized);
- mState.mResourceNeedsInit.reset(bufferIndex);
- }
- break;
- }
- case GL_DEPTH:
- {
- if (mState.mDepthAttachment.isAttached())
- {
- mState.mDepthAttachment.setInitState(InitState::Initialized);
- mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
- }
- break;
- }
- case GL_STENCIL:
- {
- if (mState.mStencilAttachment.isAttached())
- {
- mState.mStencilAttachment.setInitState(InitState::Initialized);
- mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
- }
- break;
- }
- case GL_DEPTH_STENCIL:
- {
- if (mState.mDepthAttachment.isAttached())
- {
- mState.mDepthAttachment.setInitState(InitState::Initialized);
- mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
- }
- if (mState.mStencilAttachment.isAttached())
- {
- mState.mStencilAttachment.setInitState(InitState::Initialized);
- mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
- }
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
-}
-
Box Framebuffer::getDimensions() const
{
return mState.getDimensions();
@@ -2760,69 +2766,6 @@ GLuint Framebuffer::getSupportedFoveationFeatures() const
return mState.mFoveationState.getSupportedFoveationFeatures();
}
-angle::Result Framebuffer::ensureBufferInitialized(const Context *context,
- GLenum bufferType,
- GLint bufferIndex)
-{
- ASSERT(context->isRobustResourceInitEnabled());
-
- if (mState.mResourceNeedsInit.none())
- {
- return angle::Result::Continue;
- }
-
- switch (bufferType)
- {
- case GL_COLOR:
- {
- ASSERT(bufferIndex < static_cast<GLint>(mState.mColorAttachments.size()));
- if (mState.mResourceNeedsInit[bufferIndex])
- {
- ANGLE_TRY(InitAttachment(context, &mState.mColorAttachments[bufferIndex]));
- mState.mResourceNeedsInit.reset(bufferIndex);
- }
- break;
- }
- case GL_DEPTH:
- {
- if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
- {
- ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
- mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
- }
- break;
- }
- case GL_STENCIL:
- {
- if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
- {
- ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
- mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
- }
- break;
- }
- case GL_DEPTH_STENCIL:
- {
- if (mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
- {
- ANGLE_TRY(InitAttachment(context, &mState.mDepthAttachment));
- mState.mResourceNeedsInit.reset(DIRTY_BIT_DEPTH_ATTACHMENT);
- }
- if (mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
- {
- ANGLE_TRY(InitAttachment(context, &mState.mStencilAttachment));
- mState.mResourceNeedsInit.reset(DIRTY_BIT_STENCIL_ATTACHMENT);
- }
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
-
- return angle::Result::Continue;
-}
-
bool Framebuffer::partialBufferClearNeedsInit(const Context *context, GLenum bufferType)
{
if (!context->isRobustResourceInitEnabled() || mState.mResourceNeedsInit.none())
diff --git a/src/libANGLE/Framebuffer.h b/src/libANGLE/Framebuffer.h
index ebadf88af5..f690cf4932 100644
--- a/src/libANGLE/Framebuffer.h
+++ b/src/libANGLE/Framebuffer.h
@@ -521,11 +521,7 @@ class Framebuffer final : public angle::ObserverInterface,
bool isMultiview,
GLsizei samples);
- void markDrawAttachmentsInitialized(bool color, bool depth, bool stencil);
- void markBufferInitialized(GLenum bufferType, GLint bufferIndex);
- angle::Result ensureBufferInitialized(const Context *context,
- GLenum bufferType,
- GLint bufferIndex);
+ void markAttachmentsInitialized(const DrawBufferMask &color, bool depth, bool stencil);
// Checks that we have a partially masked clear:
// * some color channels are masked out
diff --git a/src/libANGLE/FramebufferAttachment.cpp b/src/libANGLE/FramebufferAttachment.cpp
index 04650e9f46..e8e75428a6 100644
--- a/src/libANGLE/FramebufferAttachment.cpp
+++ b/src/libANGLE/FramebufferAttachment.cpp
@@ -293,7 +293,7 @@ InitState FramebufferAttachment::initState() const
: InitState::Initialized;
}
-angle::Result FramebufferAttachment::initializeContents(const Context *context)
+angle::Result FramebufferAttachment::initializeContents(const Context *context) const
{
ASSERT(mResource);
ANGLE_TRY(mResource->initializeContents(context, mTarget.binding(), mTarget.textureIndex()));
diff --git a/src/libANGLE/FramebufferAttachment.h b/src/libANGLE/FramebufferAttachment.h
index d21095005f..4c1537547a 100644
--- a/src/libANGLE/FramebufferAttachment.h
+++ b/src/libANGLE/FramebufferAttachment.h
@@ -151,7 +151,7 @@ class FramebufferAttachment final
const egl::Surface *getSurface() const;
FramebufferAttachmentObject *getResource() const;
InitState initState() const;
- angle::Result initializeContents(const Context *context);
+ angle::Result initializeContents(const Context *context) const;
void setInitState(InitState initState) const;
// "T" must be static_castable from FramebufferAttachmentRenderTarget
diff --git a/src/libANGLE/renderer/FramebufferImpl.cpp b/src/libANGLE/renderer/FramebufferImpl.cpp
index 1cef167d9d..90ab66aa15 100644
--- a/src/libANGLE/renderer/FramebufferImpl.cpp
+++ b/src/libANGLE/renderer/FramebufferImpl.cpp
@@ -10,9 +10,49 @@
namespace rx
{
+namespace
+{
+angle::Result InitAttachment(const gl::Context *context,
+ const gl::FramebufferAttachment *attachment)
+{
+ ASSERT(attachment->isAttached());
+ if (attachment->initState() == gl::InitState::MayNeedInit)
+ {
+ ANGLE_TRY(attachment->initializeContents(context));
+ }
+ return angle::Result::Continue;
+}
+} // namespace
+
angle::Result FramebufferImpl::onLabelUpdate(const gl::Context *context)
{
return angle::Result::Continue;
}
+angle::Result FramebufferImpl::ensureAttachmentsInitialized(
+ const gl::Context *context,
+ const gl::DrawBufferMask &colorAttachments,
+ bool depth,
+ bool stencil)
+{
+ // Default implementation iterates over the attachments and individually initializes them
+
+ for (auto colorIndex : colorAttachments)
+ {
+ ANGLE_TRY(InitAttachment(context, mState.getColorAttachment(colorIndex)));
+ }
+
+ if (depth)
+ {
+ ANGLE_TRY(InitAttachment(context, mState.getDepthAttachment()));
+ }
+
+ if (stencil)
+ {
+ ANGLE_TRY(InitAttachment(context, mState.getStencilAttachment()));
+ }
+
+ return angle::Result::Continue;
+}
+
} // namespace rx
diff --git a/src/libANGLE/renderer/FramebufferImpl.h b/src/libANGLE/renderer/FramebufferImpl.h
index 0ecfaba4f2..dbace39b76 100644
--- a/src/libANGLE/renderer/FramebufferImpl.h
+++ b/src/libANGLE/renderer/FramebufferImpl.h
@@ -83,6 +83,11 @@ class FramebufferImpl : angle::NonCopyable
virtual gl::FramebufferStatus checkStatus(const gl::Context *context) const = 0;
+ virtual angle::Result ensureAttachmentsInitialized(const gl::Context *context,
+ const gl::DrawBufferMask &colorAttachments,
+ bool depth,
+ bool stencil);
+
virtual angle::Result syncState(const gl::Context *context,
GLenum binding,
const gl::Framebuffer::DirtyBits &dirtyBits,
diff --git a/src/libANGLE/renderer/gl/BlitGL.cpp b/src/libANGLE/renderer/gl/BlitGL.cpp
index c8b08e7d5a..5e60cae617 100644
--- a/src/libANGLE/renderer/gl/BlitGL.cpp
+++ b/src/libANGLE/renderer/gl/BlitGL.cpp
@@ -232,6 +232,32 @@ angle::Result UnbindAttachments(const gl::Context *context,
return angle::Result::Continue;
}
+angle::Result CheckIfAttachmentNeedsClearing(const gl::Context *context,
+ const gl::FramebufferAttachment *attachment,
+ bool *needsClearInit)
+{
+ if (attachment->initState() == gl::InitState::Initialized)
+ {
+ *needsClearInit = false;
+ return angle::Result::Continue;
+ }
+
+ // Special case for 2D array and 3D textures. The init state tracks initialization for all
+ // layers but only one will be cleared by a clear call. Initialize those entire textures
+ // here.
+ if (attachment->type() == GL_TEXTURE &&
+ (attachment->getTextureImageIndex().getTarget() == gl::TextureTarget::_2DArray ||
+ attachment->getTextureImageIndex().getTarget() == gl::TextureTarget::_3D))
+ {
+ ANGLE_TRY(attachment->initializeContents(context));
+ *needsClearInit = false;
+ return angle::Result::Continue;
+ }
+
+ *needsClearInit = true;
+ return angle::Result::Continue;
+}
+
} // anonymous namespace
BlitGL::BlitGL(const FunctionsGL *functions,
@@ -1052,19 +1078,113 @@ angle::Result BlitGL::clearRenderbuffer(const gl::Context *context,
}
angle::Result BlitGL::clearFramebuffer(const gl::Context *context,
- bool colorClear,
+ const gl::DrawBufferMask &colorAttachments,
bool depthClear,
bool stencilClear,
FramebufferGL *source)
{
// initializeResources skipped because no local state is used
+ bool hasIntegerColorAttachments = false;
+
+ // Filter the color attachments for ones that actually have an init state of uninitialized.
+ gl::DrawBufferMask uninitializedColorAttachments;
+ for (size_t colorAttachmentIdx : colorAttachments)
+ {
+ bool needsInit = false;
+ const gl::FramebufferAttachment *attachment =
+ source->getState().getColorAttachment(colorAttachmentIdx);
+ ANGLE_TRY(CheckIfAttachmentNeedsClearing(context, attachment, &needsInit));
+ uninitializedColorAttachments[colorAttachmentIdx] = needsInit;
+ if (needsInit && (attachment->getComponentType() == GL_INT ||
+ attachment->getComponentType() == GL_UNSIGNED_INT))
+ {
+ hasIntegerColorAttachments = true;
+ }
+ }
+
+ bool depthNeedsInit = false;
+ if (depthClear)
+ {
+ ANGLE_TRY(CheckIfAttachmentNeedsClearing(context, source->getState().getDepthAttachment(),
+ &depthNeedsInit));
+ }
+
+ bool stencilNeedsInit = false;
+ if (stencilClear)
+ {
+ ANGLE_TRY(CheckIfAttachmentNeedsClearing(context, source->getState().getStencilAttachment(),
+ &stencilNeedsInit));
+ }
+
// Clear all attachments
GLbitfield clearMask = 0;
- ANGLE_TRY(SetClearState(mStateManager, colorClear, depthClear, stencilClear, &clearMask));
+ ANGLE_TRY(SetClearState(mStateManager, uninitializedColorAttachments.any(), depthNeedsInit,
+ stencilNeedsInit, &clearMask));
mStateManager->bindFramebuffer(GL_FRAMEBUFFER, source->getFramebufferID());
- ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
+
+ // If we're not clearing all attached color attachments, we need to clear them individually with
+ // glClearBuffer*
+ if ((clearMask & GL_COLOR_BUFFER_BIT) &&
+ (uninitializedColorAttachments != source->getState().getColorAttachmentsMask() ||
+ uninitializedColorAttachments != source->getState().getEnabledDrawBuffers() ||
+ hasIntegerColorAttachments))
+ {
+ for (size_t colorAttachmentIdx : uninitializedColorAttachments)
+ {
+ const gl::FramebufferAttachment *attachment =
+ source->getState().getColorAttachment(colorAttachmentIdx);
+ if (attachment->initState() == gl::InitState::Initialized)
+ {
+ continue;
+ }
+
+ switch (attachment->getComponentType())
+ {
+ case GL_UNSIGNED_NORMALIZED:
+ case GL_SIGNED_NORMALIZED:
+ case GL_FLOAT:
+ {
+ constexpr GLfloat clearValue[] = {0, 0, 0, 0};
+ ANGLE_GL_TRY(context,
+ mFunctions->clearBufferfv(
+ GL_COLOR, static_cast<GLint>(colorAttachmentIdx), clearValue));
+ }
+ break;
+
+ case GL_INT:
+ {
+ constexpr GLint clearValue[] = {0, 0, 0, 0};
+ ANGLE_GL_TRY(context,
+ mFunctions->clearBufferiv(
+ GL_COLOR, static_cast<GLint>(colorAttachmentIdx), clearValue));
+ }
+ break;
+
+ case GL_UNSIGNED_INT:
+ {
+ constexpr GLuint clearValue[] = {0, 0, 0, 0};
+ ANGLE_GL_TRY(context,
+ mFunctions->clearBufferuiv(
+ GL_COLOR, static_cast<GLint>(colorAttachmentIdx), clearValue));
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // Remove color buffer bit and clear the rest of the attachments with glClear
+ clearMask = clearMask & ~GL_COLOR_BUFFER_BIT;
+ }
+
+ if (clearMask != 0)
+ {
+ ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
+ }
return angle::Result::Continue;
}
diff --git a/src/libANGLE/renderer/gl/BlitGL.h b/src/libANGLE/renderer/gl/BlitGL.h
index 4229488f86..632d3a8bf0 100644
--- a/src/libANGLE/renderer/gl/BlitGL.h
+++ b/src/libANGLE/renderer/gl/BlitGL.h
@@ -151,7 +151,7 @@ class BlitGL : angle::NonCopyable
GLenum sizedInternalFormat);
angle::Result clearFramebuffer(const gl::Context *context,
- bool colorClear,
+ const gl::DrawBufferMask &colorAttachments,
bool depthClear,
bool stencilClear,
FramebufferGL *source);
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.cpp b/src/libANGLE/renderer/gl/FramebufferGL.cpp
index 6fe40a0408..8374185892 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.cpp
+++ b/src/libANGLE/renderer/gl/FramebufferGL.cpp
@@ -1303,6 +1303,24 @@ gl::FramebufferStatus FramebufferGL::checkStatus(const gl::Context *context) con
return gl::FramebufferStatus::Complete();
}
+angle::Result FramebufferGL::ensureAttachmentsInitialized(
+ const gl::Context *context,
+ const gl::DrawBufferMask &colorAttachments,
+ bool depth,
+ bool stencil)
+{
+ if (colorAttachments != getState().getEnabledDrawBuffers())
+ {
+ // Fall back to the default implementation when there are gaps in the enabled draw buffers
+ // to avoid modifying the draw buffer state.
+ return FramebufferImpl::ensureAttachmentsInitialized(context, colorAttachments, depth,
+ stencil);
+ }
+
+ BlitGL *blitter = GetBlitGL(context);
+ return blitter->clearFramebuffer(context, colorAttachments, depth, stencil, this);
+}
+
angle::Result FramebufferGL::syncState(const gl::Context *context,
GLenum binding,
const gl::Framebuffer::DirtyBits &dirtyBits,
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.h b/src/libANGLE/renderer/gl/FramebufferGL.h
index 48a1877201..d2f7c17680 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.h
+++ b/src/libANGLE/renderer/gl/FramebufferGL.h
@@ -81,6 +81,11 @@ class FramebufferGL : public FramebufferImpl
gl::FramebufferStatus checkStatus(const gl::Context *context) const override;
+ angle::Result ensureAttachmentsInitialized(const gl::Context *context,
+ const gl::DrawBufferMask &colorAttachments,
+ bool depth,
+ bool stencil) override;
+
angle::Result syncState(const gl::Context *context,
GLenum binding,
const gl::Framebuffer::DirtyBits &dirtyBits,
diff --git a/src/libANGLE/renderer/gl/SurfaceGL.cpp b/src/libANGLE/renderer/gl/SurfaceGL.cpp
index 36dc8347ff..deabd7eb80 100644
--- a/src/libANGLE/renderer/gl/SurfaceGL.cpp
+++ b/src/libANGLE/renderer/gl/SurfaceGL.cpp
@@ -46,12 +46,17 @@ angle::Result SurfaceGL::initializeContents(const gl::Context *context,
switch (binding)
{
case GL_BACK:
- ANGLE_TRY(blitter->clearFramebuffer(context, true, false, false, framebufferGL));
- break;
+ {
+ gl::DrawBufferMask colorAttachments{0};
+ ANGLE_TRY(
+ blitter->clearFramebuffer(context, colorAttachments, false, false, framebufferGL));
+ }
+ break;
case GL_DEPTH:
case GL_STENCIL:
- ANGLE_TRY(blitter->clearFramebuffer(context, false, true, true, framebufferGL));
+ ANGLE_TRY(blitter->clearFramebuffer(context, gl::DrawBufferMask(), true, true,
+ framebufferGL));
break;
default:
diff --git a/src/tests/gl_tests/RobustResourceInitTest.cpp b/src/tests/gl_tests/RobustResourceInitTest.cpp
index 245455f4a8..f38a0f99f4 100644
--- a/src/tests/gl_tests/RobustResourceInitTest.cpp
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -2009,6 +2009,65 @@ TEST_P(RobustResourceInitTestES3, CompressedSubImage)
}
}
+// Test drawing to a framebuffer with not all draw buffers enabled
+TEST_P(RobustResourceInitTestES3, SparseDrawBuffers)
+{
+ constexpr char kVS[] = R"(#version 300 es
+void main() {
+ gl_PointSize = 100.0;
+ gl_Position = vec4(0, 0, 0, 1);
+})";
+
+ constexpr char kFS[] = R"(#version 300 es
+precision highp float;
+layout(location = 1) out vec4 output1;
+layout(location = 3) out vec4 output2;
+void main()
+{
+ output1 = vec4(0.0, 1.0, 0.0, 1.0);
+ output2 = vec4(0.0, 0.0, 1.0, 1.0);
+})";
+
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
+ glUseProgram(program);
+
+ GLFramebuffer fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ std::vector<GLTexture> textures(4);
+ for (size_t i = 0; i < textures.size(); i++)
+ {
+ glBindTexture(GL_TEXTURE_2D, textures[i]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i],
+ 0);
+ }
+
+ glViewport(0, 0, 1, 1);
+
+ constexpr GLenum drawBuffers[4] = {
+ GL_NONE,
+ GL_COLOR_ATTACHMENT1,
+ GL_NONE,
+ GL_COLOR_ATTACHMENT3,
+ };
+ glDrawBuffers(4, drawBuffers);
+
+ glDrawArrays(GL_POINTS, 0, 1);
+
+ const GLColor expectedColors[4] = {
+ GLColor::transparentBlack,
+ GLColor::green,
+ GLColor::transparentBlack,
+ GLColor::blue,
+ };
+ for (size_t i = 0; i < textures.size(); i++)
+ {
+ glReadBuffer(GL_COLOR_ATTACHMENT0 + i);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColors[i]) << " at attachment " << i;
+ }
+}
+
// Tests that a partial scissor still initializes contents as expected.
TEST_P(RobustResourceInitTest, ClearWithScissor)
{