Commit e39d055d by Kenneth Russell Committed by Commit Bot

Fix robust resource initialization with clipped CopyTexSubImage2D.

When CopyTexSubImage2D calls were clipped against the bounds of the read framebuffer by the underlying renderer backends, the robust resource initialization code assumed that the original destination area would be overwritten by the renderer, which was not the case. Add new tests which were previously failing on macOS with the ES2_OpenGL and ES3_OpenGL backends. The Metal backend is still failing and a follow-on bug has been filed. Bug: angleproject:4504 Change-Id: I34821dd90597f31b3cbf0921b94756556e485c91 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2116873Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Commit-Queue: Kenneth Russell <kbr@chromium.org>
parent ed0e932c
...@@ -1192,7 +1192,27 @@ angle::Result Texture::copySubImage(Context *context, ...@@ -1192,7 +1192,27 @@ angle::Result Texture::copySubImage(Context *context,
{ {
ASSERT(TextureTargetToType(index.getTarget()) == mState.mType); ASSERT(TextureTargetToType(index.getTarget()) == mState.mType);
Box destBox(destOffset.x, destOffset.y, destOffset.z, sourceArea.width, sourceArea.height, 1); // Most if not all renderers clip these copies to the size of the source framebuffer, leaving
// other pixels untouched. For safety in robust resource initialization, assume that that
// clipping is going to occur when computing the region for which to ensure initialization. If
// the copy lies entirely off the source framebuffer, initialize as though a zero-size box is
// going to be set during the copy operation. Note that this assumes that
// ensureSubImageInitialized ensures initialization of the entire destination texture, and not
// just a sub-region.
Box destBox;
if (context->isRobustResourceInitEnabled())
{
Extents fbSize = source->getReadColorAttachment()->getSize();
Rectangle clippedArea;
if (ClipRectangle(sourceArea, Rectangle(0, 0, fbSize.width, fbSize.height), &clippedArea))
{
const Offset clippedOffset(destOffset.x + clippedArea.x - sourceArea.x,
destOffset.y + clippedArea.y - sourceArea.y, 0);
destBox = Box(clippedOffset.x, clippedOffset.y, clippedOffset.z, clippedArea.width,
clippedArea.height, 1);
}
}
ANGLE_TRY( ANGLE_TRY(
ensureSubImageInitialized(context, index.getTarget(), index.getLevelIndex(), destBox)); ensureSubImageInitialized(context, index.getTarget(), index.getLevelIndex(), destBox));
...@@ -1859,6 +1879,8 @@ angle::Result Texture::ensureSubImageInitialized(const Context *context, ...@@ -1859,6 +1879,8 @@ angle::Result Texture::ensureSubImageInitialized(const Context *context,
area.depth == desc.size.depth; area.depth == desc.size.depth;
if (!coversWholeImage) if (!coversWholeImage)
{ {
// NOTE: do not optimize this to only initialize the passed area of the texture, or the
// initialization logic in copySubImage will be incorrect.
ANGLE_TRY(initializeContents(context, imageIndex)); ANGLE_TRY(initializeContents(context, imageIndex));
} }
setInitState(imageIndex, InitState::Initialized); setInitState(imageIndex, InitState::Initialized);
......
...@@ -749,8 +749,8 @@ angle::Result TextureGL::copySubImage(const gl::Context *context, ...@@ -749,8 +749,8 @@ angle::Result TextureGL::copySubImage(const gl::Context *context,
const gl::Rectangle &sourceArea, const gl::Rectangle &sourceArea,
gl::Framebuffer *source) gl::Framebuffer *source)
{ {
const FunctionsGL *functions = GetFunctionsGL(context); const FunctionsGL *functions = GetFunctionsGL(context);
StateManagerGL *stateManager = GetStateManagerGL(context); StateManagerGL *stateManager = GetStateManagerGL(context);
const angle::FeaturesGL &features = GetFeaturesGL(context); const angle::FeaturesGL &features = GetFeaturesGL(context);
gl::TextureTarget target = index.getTarget(); gl::TextureTarget target = index.getTarget();
......
...@@ -291,6 +291,8 @@ class RobustResourceInitTest : public ANGLETest ...@@ -291,6 +291,8 @@ class RobustResourceInitTest : public ANGLETest
template <typename ClearFunc> template <typename ClearFunc>
void maskedStencilClear(ClearFunc clearFunc); void maskedStencilClear(ClearFunc clearFunc);
void copyTexSubImage2DCustomFBOTest(int offsetX, int offsetY);
}; };
class RobustResourceInitTestES3 : public RobustResourceInitTest class RobustResourceInitTestES3 : public RobustResourceInitTest
...@@ -1531,6 +1533,60 @@ TEST_P(RobustResourceInitTest, CopyTexSubImage2D) ...@@ -1531,6 +1533,60 @@ TEST_P(RobustResourceInitTest, CopyTexSubImage2D)
VerifyRGBA8PixelRect<kDestSize>(destInitTest); VerifyRGBA8PixelRect<kDestSize>(destInitTest);
} }
void RobustResourceInitTest::copyTexSubImage2DCustomFBOTest(int offsetX, int offsetY)
{
const int texSize = 512;
const int fboSize = 16;
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture.get());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texSize, texSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
ASSERT_GL_NO_ERROR();
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, fboSize, fboSize);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
renderbuffer.get());
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, offsetX, offsetY, texSize, texSize);
ASSERT_GL_NO_ERROR();
GLFramebuffer readbackFBO;
glBindFramebuffer(GL_FRAMEBUFFER, readbackFBO.get());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
checkCustomFramebufferNonZeroPixels(texSize, texSize, -offsetX, -offsetY, fboSize, fboSize,
GLColor::red);
}
// Test CopyTexSubImage2D clipped to size of custom FBO, zero x/y source offset.
TEST_P(RobustResourceInitTest, CopyTexSubImage2DCustomFBOZeroOffsets)
{
// TODO(anglebug.com/4507): pass this test on the Metal backend.
ANGLE_SKIP_TEST_IF(IsMetal());
copyTexSubImage2DCustomFBOTest(0, 0);
}
// Test CopyTexSubImage2D clipped to size of custom FBO, negative x/y source offset.
TEST_P(RobustResourceInitTest, CopyTexSubImage2DCustomFBONegativeOffsets)
{
// TODO(anglebug.com/4507): pass this test on the Metal backend.
ANGLE_SKIP_TEST_IF(IsMetal());
copyTexSubImage2DCustomFBOTest(-8, -8);
}
// Tests that calling CopyTexSubImage3D will initialize the source & destination. // Tests that calling CopyTexSubImage3D will initialize the source & destination.
TEST_P(RobustResourceInitTestES3, CopyTexSubImage3D) TEST_P(RobustResourceInitTestES3, CopyTexSubImage3D)
{ {
......
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