Sync framebuffer bindings in glInvalidateFramebuffer

If a framebuffer binding change is followed by glInvalidateFramebuffer,
ANGLE was not syncing the framebuffer binding.

- This means that invalidation was being done on the previous
  framebuffer.
- Paired with deferred clears, this was causing ContextVk to start a
  render pass on the previous, potentially deleted, framebuffer.

Bug: chromium:1267027
Change-Id: I092a0c8dd764db9e49258b694c970babb19cf24b
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3266175
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Shahbaz Youssefi 2021-11-08 15:24:09 -05:00 коммит произвёл Angle LUCI CQ
Родитель a9f24fa529
Коммит 0e20c68092
6 изменённых файлов: 49 добавлений и 7 удалений

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

@ -645,6 +645,9 @@ void Context::initializeDefaultResources()
mCopyImageDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
mCopyImageDirtyObjects.set(State::DIRTY_OBJECT_READ_FRAMEBUFFER);
mInvalidateDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
mInvalidateDirtyBits.set(State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING);
// Initialize overlay after implementation is initialized.
ANGLE_CONTEXT_TRY(mOverlay.init(this));
}
@ -4139,6 +4142,13 @@ ANGLE_INLINE angle::Result Context::prepareForDispatch()
return syncDirtyBits(mComputeDirtyBits, Command::Dispatch);
}
angle::Result Context::prepareForInvalidate(GLenum target)
{
// Only sync the FBO that's being invalidated
ANGLE_TRY(mState.syncDirtyObject(this, target));
return syncDirtyBits(mInvalidateDirtyBits, Command::Invalidate);
}
angle::Result Context::syncState(const State::DirtyBits &bitMask,
const State::DirtyObjects &objectMask,
Command command)
@ -4780,14 +4790,12 @@ void Context::readBuffer(GLenum mode)
void Context::discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments)
{
// Only sync the FBO
ANGLE_CONTEXT_TRY(mState.syncDirtyObject(this, target));
Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer);
// The specification isn't clear what should be done when the framebuffer isn't complete.
// We leave it up to the framebuffer implementation to decide what to do.
ANGLE_CONTEXT_TRY(prepareForInvalidate(target));
ANGLE_CONTEXT_TRY(framebuffer->discard(this, numAttachments, attachments));
}
@ -4804,8 +4812,7 @@ void Context::invalidateFramebuffer(GLenum target,
return;
}
// Only sync the FBO
ANGLE_CONTEXT_TRY(mState.syncDirtyObject(this, target));
ANGLE_CONTEXT_TRY(prepareForInvalidate(target));
ANGLE_CONTEXT_TRY(framebuffer->invalidate(this, numAttachments, attachments));
}
@ -4826,8 +4833,7 @@ void Context::invalidateSubFramebuffer(GLenum target,
}
Rectangle area(x, y, width, height);
// Only sync the FBO
ANGLE_CONTEXT_TRY(mState.syncDirtyObject(this, target));
ANGLE_CONTEXT_TRY(prepareForInvalidate(target));
ANGLE_CONTEXT_TRY(framebuffer->invalidateSub(this, numAttachments, attachments, area));
}

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

@ -490,6 +490,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
angle::Result prepareForCopyImage();
angle::Result prepareForDispatch();
angle::Result prepareForInvalidate(GLenum target);
MemoryProgramCache *getMemoryProgramCache() const { return mMemoryProgramCache; }
std::mutex &getProgramCacheMutex() const;
@ -770,6 +771,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
State::DirtyObjects mComputeDirtyObjects;
State::DirtyBits mCopyImageDirtyBits;
State::DirtyObjects mCopyImageDirtyObjects;
State::DirtyBits mInvalidateDirtyBits;
// Binding to container objects that use dependent state updates.
angle::ObserverBinding mVertexArrayObserverBinding;

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

@ -38,6 +38,7 @@ enum class Command
Dispatch,
Draw,
GenerateMipmap,
Invalidate,
ReadPixels,
TexImage,
Other

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

@ -5600,6 +5600,8 @@ angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea,
vk::CommandBuffer **commandBufferOut,
bool *renderPassDescChangedOut)
{
ASSERT(mDrawFramebuffer == vk::GetImpl(mState.getDrawFramebuffer()));
ANGLE_TRY(mDrawFramebuffer->startNewRenderPass(this, renderArea, &mRenderPassCommandBuffer,
renderPassDescChangedOut));

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

@ -210,6 +210,8 @@
5233 MAC METAL : WebGL2ValidationStateChangeTest.MultiAttachmentDrawFramebufferNegativeAPI/* = SKIP
6669 MAC METAL : CopyTextureTestES3.InvalidateCopyThenBlend/* = SKIP
// D3D
6432 WIN D3D9 : GLSLTest.HandleExcessiveLoopBug/* = SKIP
6091 WIN D3D11 : GLSLTest_ES3.InitGlobalComplexConstant/* = SKIP

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

@ -7108,6 +7108,35 @@ void main()
glDrawArrays(GL_POINTS, 0, 1);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Regression test for a bug where the framebuffer binding was not synced during invalidate when a
// clear operation was deferred.
TEST_P(SimpleStateChangeTestES3, ChangeFramebufferThenInvalidateWithClear)
{
// Clear the default framebuffer.
glClear(GL_COLOR_BUFFER_BIT);
// Start rendering to another framebuffer
GLFramebuffer fbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
GLRenderbuffer rbo;
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Red());
glUseProgram(program);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Switch back to the default framebuffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// Invalidate it. Don't invalidate color, as that's the one being cleared.
constexpr GLenum kAttachment = GL_DEPTH;
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &kAttachment);
EXPECT_GL_NO_ERROR();
}
} // anonymous namespace
ANGLE_INSTANTIATE_TEST_ES2(StateChangeTest);