Add support for GL_MESA_framebuffer_flip_y 3/*
This is a third CL that adds tests that exercise
the extension in various use cases.
Bug: chromium:1231934
Change-Id: Iae3192cd0985150b6844a2855a9a048a54353655
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3365195
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
Commit-Queue: Maksim Sisov <msisov@igalia.com>
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 80af233..e6e6b3c 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -1801,13 +1801,8 @@
extensions->YUVTargetEXT = functions->hasGLESExtension("GL_EXT_YUV_target");
// GL_MESA_framebuffer_flip_y
- if (functions->isAtLeastGL(gl::Version(4, 3)) ||
- functions->hasGLExtension("GL_MESA_framebuffer_flip_y") ||
- functions->isAtLeastGLES(gl::Version(3, 1)) ||
- functions->hasGLESExtension("GL_MESA_framebuffer_flip_y"))
- {
- extensions->framebufferFlipYMESA = true;
- }
+ extensions->framebufferFlipYMESA = functions->hasGLESExtension("GL_MESA_framebuffer_flip_y") ||
+ functions->hasGLExtension("GL_MESA_framebuffer_flip_y");
// GL_KHR_parallel_shader_compile
extensions->parallelShaderCompileKHR = true;
diff --git a/src/tests/gl_tests/BlitFramebufferANGLETest.cpp b/src/tests/gl_tests/BlitFramebufferANGLETest.cpp
index 4abb7d8..06afbf8 100644
--- a/src/tests/gl_tests/BlitFramebufferANGLETest.cpp
+++ b/src/tests/gl_tests/BlitFramebufferANGLETest.cpp
@@ -350,6 +350,67 @@
return true;
}
+ void BlitStencilTestHelper(bool mesaYFlip)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
+
+ if (mesaYFlip)
+ {
+ ASSERT_TRUE(IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ }
+
+ glClearColor(0.0, 1.0, 0.0, 1.0);
+ glClearStencil(0x0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // Scissor half the screen so we fill the stencil only halfway
+ glScissor(0, 0, getWindowWidth(), getWindowHeight() / 2);
+ glEnable(GL_SCISSOR_TEST);
+
+ // fill the stencil buffer with 0x1
+ glStencilFunc(GL_ALWAYS, 0x1, 0xFF);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ glEnable(GL_STENCIL_TEST);
+ drawQuad(mRedProgram, essl1_shaders::PositionAttrib(), 0.3f);
+
+ glDisable(GL_SCISSOR_TEST);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
+
+ // These clears are not useful in theory because we're copying over them, but its
+ // helpful in debugging if we see white in any result.
+ glClearColor(1.0, 1.0, 1.0, 1.0);
+ glClearStencil(0x0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
+ getWindowHeight(), GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
+ GL_NEAREST);
+
+ EXPECT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
+
+ EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
+ EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
+ EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
+
+ glStencilFunc(GL_EQUAL, 0x1, 0xFF); // only pass if stencil buffer at pixel reads 0x1
+
+ drawQuad(mBlueProgram, essl1_shaders::PositionAttrib(),
+ 0.8f); // blue quad will draw if stencil buffer was copied
+
+ glDisable(GL_STENCIL_TEST);
+
+ EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
+ }
+
GLuint mCheckerProgram;
GLuint mBlueProgram;
GLuint mRedProgram;
@@ -501,6 +562,267 @@
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
}
+// Blit color to default framebuffer from another framebuffer with GL_MESA_framebuffer_flip_y.
+TEST_P(BlitFramebufferANGLETest, BlitColorWithMesaYFlipSrc)
+{
+ // OpenGL ES 3.0 / GL_NV_framebuffer_blit required for flip.
+ ANGLE_SKIP_TEST_IF(
+ (getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_NV_framebuffer_blit")) ||
+ !IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
+
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ EXPECT_GL_NO_ERROR();
+
+ drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.8f);
+
+ EXPECT_GL_NO_ERROR();
+
+ // Blit to default from y-flipped.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
+
+ const int fboTargetWidth = getWindowHeight() / 2;
+ const int fboTargetHeight = getWindowHeight() / 2;
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, fboTargetWidth,
+ fboTargetHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ EXPECT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
+
+ EXPECT_PIXEL_COLOR_EQ(fboTargetWidth / 4, fboTargetHeight / 4, GLColor::red);
+ EXPECT_PIXEL_COLOR_EQ(fboTargetWidth / 4, 3 * fboTargetHeight / 4, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(3 * fboTargetWidth / 4, fboTargetHeight / 4, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(3 * fboTargetWidth / 4, 3 * fboTargetHeight / 4, GLColor::yellow);
+}
+
+// Blit color to y-flipped with GL_MESA_framebuffer_flip_y framebuffer from normal framebuffer.
+TEST_P(BlitFramebufferANGLETest, BlitColorWithMesaYFlipDst)
+{
+ // OpenGL ES 3.0 / GL_NV_framebuffer_blit required for flip.
+ ANGLE_SKIP_TEST_IF(
+ (getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_NV_framebuffer_blit")) ||
+ !IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.8f);
+
+ EXPECT_GL_NO_ERROR();
+
+ // Blit to default from y-flipped.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mOriginalFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mUserFBO);
+
+ glFramebufferParameteriMESA(GL_DRAW_FRAMEBUFFER_ANGLE, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ const int fboTargetWidth = getWindowWidth() / 2;
+ const int fboTargetHeight = getWindowHeight();
+
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, fboTargetWidth,
+ fboTargetHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), getWindowWidth() / 2, 0,
+ getWindowWidth(), getWindowHeight() / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ glFramebufferParameteriMESA(GL_DRAW_FRAMEBUFFER_ANGLE, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+
+ EXPECT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
+
+ // Left side have inverted checker pattern.
+ EXPECT_PIXEL_COLOR_EQ(fboTargetWidth / 4, fboTargetHeight / 4, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(fboTargetWidth / 4, 3 * fboTargetHeight / 4, GLColor::red);
+ EXPECT_PIXEL_COLOR_EQ(3 * fboTargetWidth / 4, fboTargetHeight / 4, GLColor::yellow);
+ EXPECT_PIXEL_COLOR_EQ(3 * fboTargetWidth / 4, 3 * fboTargetHeight / 4, GLColor::blue);
+
+ // Right side is split to 2 parts where upper part have non y-flipped checker pattern and the
+ // bottom one has white color.
+ EXPECT_PIXEL_COLOR_EQ(5 * getWindowWidth() / 8, 5 * getWindowHeight() / 8, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(5 * getWindowWidth() / 8, 7 * getWindowHeight() / 8, GLColor::red);
+ EXPECT_PIXEL_COLOR_EQ(7 * getWindowWidth() / 8, 5 * getWindowHeight() / 8, GLColor::yellow);
+ EXPECT_PIXEL_COLOR_EQ(7 * getWindowWidth() / 8, 7 * getWindowHeight() / 8, GLColor::blue);
+
+ EXPECT_PIXEL_RECT_EQ(4 * getWindowWidth() / 8, 0, getWindowWidth() / 4, getWindowHeight() / 2,
+ GLColor::white);
+}
+
+// Blit color to/from y-flipped with GL_MESA_framebuffer_flip_y framebuffers where dst framebuffer
+// have different size.
+TEST_P(BlitFramebufferANGLETest, BlitColorWithMesaYFlipSrcDst)
+{
+ // OpenGL ES 3.0 / GL_NV_framebuffer_blit required for flip.
+ ANGLE_SKIP_TEST_IF(
+ (getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_NV_framebuffer_blit")) ||
+ !IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ // Create a custom framebuffer as the default one cannot be flipped.
+ GLTexture tex0;
+ glBindTexture(GL_TEXTURE_2D, tex0);
+ const int fb0Width = getWindowWidth() / 2;
+ const int fb0Height = getWindowHeight() / 2;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb0Width, fb0Height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ nullptr);
+
+ GLFramebuffer fb0;
+ glBindFramebuffer(GL_FRAMEBUFFER, fb0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.8f);
+
+ EXPECT_GL_NO_ERROR();
+
+ // Blit to default from y-flipped.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, fb0);
+
+ glFramebufferParameteriMESA(GL_DRAW_FRAMEBUFFER_ANGLE, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glFramebufferParameteriMESA(GL_READ_FRAMEBUFFER_ANGLE, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ const int fboTargetWidth = fb0Width / 2;
+ const int fboTargetHeight = fb0Height;
+
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, fboTargetWidth,
+ fboTargetHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), fb0Width / 2, 0, fb0Width,
+ fb0Height / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ EXPECT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fb0);
+
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+
+ // Left side have inverted checker pattern.
+ EXPECT_PIXEL_COLOR_EQ(fboTargetWidth / 4, fboTargetHeight / 4, GLColor::red);
+ EXPECT_PIXEL_COLOR_EQ(fboTargetWidth / 4, 3 * fboTargetHeight / 4, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(3 * fboTargetWidth / 4, fboTargetHeight / 4, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(3 * fboTargetWidth / 4, 3 * fboTargetHeight / 4, GLColor::yellow);
+
+ // Right side is split to 2 parts where upper part have y-flipped checker pattern and the
+ // bottom one has white color.
+ EXPECT_PIXEL_COLOR_EQ(5 * fb0Width / 8, 5 * fb0Height / 8, GLColor::red);
+ EXPECT_PIXEL_COLOR_EQ(5 * fb0Width / 8, 7 * fb0Height / 8, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(7 * fb0Width / 8, 5 * fb0Height / 8, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(7 * fb0Width / 8, 7 * fb0Height / 8, GLColor::yellow);
+
+ EXPECT_PIXEL_RECT_EQ(4 * fb0Width / 8, 0, fb0Width / 4, fb0Height / 2, GLColor::white);
+}
+
+// Same as BlitColorWithMesaYFlip but uses an integer buffer format.
+TEST_P(BlitFramebufferANGLETest, BlitColorWithMesaYFlipInteger)
+{
+ // OpenGL ES 3.0 / GL_NV_framebuffer_blit required for flip.
+ ANGLE_SKIP_TEST_IF(
+ (getClientMajorVersion() < 3 || !IsGLExtensionEnabled("GL_NV_framebuffer_blit")) ||
+ !IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ GLTexture tex0;
+ glBindTexture(GL_TEXTURE_2D, tex0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8I, getWindowWidth(), getWindowHeight(), 0,
+ GL_RGBA_INTEGER, GL_BYTE, nullptr);
+
+ GLFramebuffer fb0;
+ glBindFramebuffer(GL_FRAMEBUFFER, fb0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.8f);
+
+ EXPECT_GL_NO_ERROR();
+
+ GLTexture tex1;
+ glBindTexture(GL_TEXTURE_2D, tex1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8I, getWindowWidth(), getWindowHeight(), 0,
+ GL_RGBA_INTEGER, GL_BYTE, nullptr);
+
+ GLFramebuffer fb1;
+ glBindFramebuffer(GL_FRAMEBUFFER, fb1);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex1, 0);
+
+ // Blit to default from y-flipped.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, fb0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, fb1);
+
+ const int fb1_target_width = getWindowHeight() / 3;
+ const int fb1_target_height = getWindowHeight() / 3;
+
+ glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, fb1_target_width,
+ fb1_target_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ EXPECT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fb1);
+
+ // The colors outside the target must remain the same.
+ EXPECT_PIXEL_8I(getWindowWidth() - 1, getWindowHeight() - 1, 0, 127, 127, 127);
+ EXPECT_PIXEL_8I(getWindowWidth() - 1, 0, 0, 127, 127, 127);
+ EXPECT_PIXEL_8I(0, getWindowHeight() - 1, 0, 127, 127, 127);
+ EXPECT_PIXEL_8I(fb1_target_width, fb1_target_height, 0, 127, 127, 127);
+
+ // While inside must change.
+ EXPECT_PIXEL_8I(fb1_target_width / 4, fb1_target_height / 4, 127, 0, 0, 127);
+ EXPECT_PIXEL_8I(fb1_target_width / 4, 3 * fb1_target_height / 4, 0, 127, 0, 127);
+ EXPECT_PIXEL_8I(3 * fb1_target_width / 4, fb1_target_height / 4, 0, 0, 127, 127);
+ EXPECT_PIXEL_8I(3 * fb1_target_width / 4, 3 * fb1_target_height / 4, 127, 127, 0, 127);
+
+ // Blit from y-flipped to default.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, fb1);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, fb0);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ // Set y-flip flag so that y-flipped frame buffer blit to the original fbo in reverse. This
+ // should result in flipping y back.
+ glFramebufferParameteriMESA(GL_DRAW_FRAMEBUFFER_ANGLE, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ glBlitFramebuffer(0, 0, fb1_target_width, fb1_target_height, 0, 0, getWindowWidth(),
+ getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ // And explicitly disable y-flip so that read does not implicitly use this flag.
+ glFramebufferParameteriMESA(GL_DRAW_FRAMEBUFFER_ANGLE, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+
+ EXPECT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fb0);
+
+ EXPECT_PIXEL_8I(getWindowWidth() / 4, getWindowHeight() / 4, 0, 127, 0, 127);
+ EXPECT_PIXEL_8I(getWindowWidth() / 4, 3 * getWindowHeight() / 4, 127, 0, 0, 127);
+ EXPECT_PIXEL_8I(3 * getWindowWidth() / 4, getWindowHeight() / 4, 127, 127, 0, 127);
+ EXPECT_PIXEL_8I(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, 0, 0, 127, 127);
+}
+
// Draw to system framebuffer, blit whole-buffer color to user-created framebuffer.
TEST_P(BlitFramebufferANGLETest, ReverseColorBlit)
{
@@ -927,58 +1249,22 @@
// http://anglebug.com/5396
ANGLE_SKIP_TEST_IF(IsAMD() && IsD3D9());
- glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
+ BlitStencilTestHelper(false /* mesaFlipY */);
+}
- glClearColor(0.0, 1.0, 0.0, 1.0);
- glClearStencil(0x0);
- glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+// Same as BlitStencil, but with y-flip flag set.
+TEST_P(BlitFramebufferANGLETest, BlitStencilWithMesaYFlip)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit") ||
+ !IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
- // Scissor half the screen so we fill the stencil only halfway
- glScissor(0, 0, getWindowWidth(), getWindowHeight() / 2);
- glEnable(GL_SCISSOR_TEST);
+ // http://anglebug.com/2205
+ ANGLE_SKIP_TEST_IF(IsIntel() && IsD3D9());
- // fill the stencil buffer with 0x1
- glStencilFunc(GL_ALWAYS, 0x1, 0xFF);
- glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
- glEnable(GL_STENCIL_TEST);
- drawQuad(mRedProgram, essl1_shaders::PositionAttrib(), 0.3f);
+ // http://anglebug.com/5396
+ ANGLE_SKIP_TEST_IF(IsAMD() && IsD3D9());
- glDisable(GL_SCISSOR_TEST);
-
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
- glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
-
- // These clears are not useful in theory because we're copying over them, but its
- // helpful in debugging if we see white in any result.
- glClearColor(1.0, 1.0, 1.0, 1.0);
- glClearStencil(0x0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
- // depth blit request should be silently ignored, because the read FBO has no depth attachment
- glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
- getWindowHeight(), GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
- GL_NEAREST);
-
- EXPECT_GL_NO_ERROR();
-
- glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
-
- EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
- EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
- EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
- EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
-
- glStencilFunc(GL_EQUAL, 0x1, 0xFF); // only pass if stencil buffer at pixel reads 0x1
-
- drawQuad(mBlueProgram, essl1_shaders::PositionAttrib(),
- 0.8f); // blue quad will draw if stencil buffer was copied
-
- glDisable(GL_STENCIL_TEST);
-
- EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
- EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
- EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
- EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
+ BlitStencilTestHelper(true /* mesaFlipY */);
}
// make sure that attempting to blit a partial depth buffer issues an error
@@ -1235,6 +1521,69 @@
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
drawQuad(checkerProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
}
+
+ void BlitDepthStencilPixelByPixelTestHelper(bool mesaYFlip)
+ {
+ if (mesaYFlip)
+ ASSERT_TRUE(IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
+
+ glViewport(0, 0, 128, 1);
+ glEnable(GL_DEPTH_TEST);
+
+ GLFramebuffer srcFramebuffer;
+ GLRenderbuffer srcRenderbuffer;
+ glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
+ if (mesaYFlip)
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glBindRenderbuffer(GL_RENDERBUFFER, srcRenderbuffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 128, 1);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
+ srcRenderbuffer);
+ glClearDepthf(1.0f);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f, 0.5f);
+ glViewport(0, 0, 256, 2);
+
+ GLFramebuffer dstFramebuffer;
+ GLRenderbuffer dstRenderbuffer;
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, dstRenderbuffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 256, 2);
+ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
+ dstRenderbuffer);
+
+ GLTexture dstColor;
+ glBindTexture(GL_TEXTURE_2D, dstColor);
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 2);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstColor, 0);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
+ glBlitFramebuffer(0, 0, 128, 1, 0, 0, 256, 2, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDepthMask(false);
+ glDepthFunc(GL_LESS);
+ drawQuad(drawRed, essl1_shaders::PositionAttrib(), -0.01f, 0.5f);
+ EXPECT_PIXEL_RECT_EQ(64, 0, 128, 1, GLColor::red);
+
+ ANGLE_GL_PROGRAM(drawBlue, essl3_shaders::vs::Simple(), essl3_shaders::fs::Blue());
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(false);
+ glDepthFunc(GL_GREATER);
+ if (mesaYFlip)
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ drawQuad(drawBlue, essl1_shaders::PositionAttrib(), 0.01f, 0.5f);
+ if (mesaYFlip)
+ EXPECT_PIXEL_RECT_EQ(64, 0, 128, 1, GLColor::green);
+ else
+ EXPECT_PIXEL_RECT_EQ(64, 0, 128, 1, GLColor::blue);
+ }
};
class BlitFramebufferTestES31 : public BlitFramebufferTest
@@ -2552,55 +2901,15 @@
// Test blitting a depthStencil buffer with multiple depth values to a larger size.
TEST_P(BlitFramebufferTest, BlitDepthStencilPixelByPixel)
{
- ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
+ BlitDepthStencilPixelByPixelTestHelper(false /* mesaYFlip */);
+}
- glViewport(0, 0, 128, 1);
- glEnable(GL_DEPTH_TEST);
+// Same as BlitDepthStencilPixelByPixel, but with y-flip flag set.
+TEST_P(BlitFramebufferTest, BlitDepthStencilPixelByPixelMesaYFlip)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
- GLFramebuffer srcFramebuffer;
- GLRenderbuffer srcRenderbuffer;
- glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
- glBindRenderbuffer(GL_RENDERBUFFER, srcRenderbuffer);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 128, 1);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
- srcRenderbuffer);
- glClearDepthf(1.0f);
- glClear(GL_DEPTH_BUFFER_BIT);
-
- drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f, 0.5f);
- glViewport(0, 0, 256, 2);
-
- GLFramebuffer dstFramebuffer;
- GLRenderbuffer dstRenderbuffer;
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
- glBindRenderbuffer(GL_RENDERBUFFER, dstRenderbuffer);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 256, 2);
- glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
- dstRenderbuffer);
-
- GLTexture dstColor;
- glBindTexture(GL_TEXTURE_2D, dstColor);
- glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 2);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstColor, 0);
-
- glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
- glBlitFramebuffer(0, 0, 128, 1, 0, 0, 256, 2, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
- GL_NEAREST);
-
- glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
- glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- glDepthMask(false);
- glDepthFunc(GL_LESS);
- drawQuad(drawRed, essl1_shaders::PositionAttrib(), -0.01f, 0.5f);
- EXPECT_PIXEL_RECT_EQ(64, 0, 128, 1, GLColor::red);
-
- ANGLE_GL_PROGRAM(drawBlue, essl3_shaders::vs::Simple(), essl3_shaders::fs::Blue());
- glEnable(GL_DEPTH_TEST);
- glDepthMask(false);
- glDepthFunc(GL_GREATER);
- drawQuad(drawBlue, essl1_shaders::PositionAttrib(), 0.01f, 0.5f);
- EXPECT_PIXEL_RECT_EQ(64, 0, 128, 1, GLColor::blue);
+ BlitDepthStencilPixelByPixelTestHelper(true /* mesaYFlip */);
}
// Test that a draw call to a small FBO followed by a resolve of a large FBO works.
@@ -2718,6 +3027,227 @@
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::yellow);
}
+// Test resolving a multisampled texture with blit. Draw flipped, resolve with read fbo flipped.
+TEST_P(BlitFramebufferTestES31, MultisampleFlippedResolveReadWithBlitAndFlippedDraw)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture;
+ GLFramebuffer resolveFBO;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture);
+ constexpr int kResolveFBOWidth = kSize - 3;
+ constexpr int kResolveFBOHeight = kSize - 2;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kResolveFBOWidth, kResolveFBOHeight, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glFramebufferParameteriMESA(GL_READ_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ glBlitFramebuffer(0, 0, kResolveFBOWidth, kResolveFBOHeight, 0, 0, kResolveFBOWidth,
+ kResolveFBOHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ constexpr uint8_t kHalfPixelGradient = 256 / kSize / 2;
+ EXPECT_PIXEL_NEAR(0, 0, kHalfPixelGradient, kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, 0, 199, kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(0, kResolveFBOHeight - 1, kHalfPixelGradient, 215, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, kResolveFBOHeight - 1, 199, 215, 0, 255, 1.0);
+}
+
+// Test resolving a multisampled texture with blit. Draw non-flipped, resolve with read fbo flipped.
+TEST_P(BlitFramebufferTestES31, MultisampleFlippedResolveReadWithBlitAndNonFlippedDraw)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ // Draw non-flipped - explicitly set y-flip to 0.
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture;
+ GLFramebuffer resolveFBO;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture);
+ constexpr int kResolveFBOWidth = kSize - 3;
+ constexpr int kResolveFBOHeight = kSize - 2;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kResolveFBOWidth, kResolveFBOHeight, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ // Resolve with read fbo flipped and draw fbo non-flipped
+ glFramebufferParameteriMESA(GL_READ_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ glBlitFramebuffer(0, 0, kResolveFBOWidth, kResolveFBOHeight, 0, 0, kResolveFBOWidth,
+ kResolveFBOHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ constexpr uint8_t kHalfPixelGradient = 256 / kSize / 2;
+ EXPECT_PIXEL_NEAR(0, 0, kHalfPixelGradient, 255 - kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, 0, 199, 255 - kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(0, kResolveFBOHeight - 1, kHalfPixelGradient, 40, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, kResolveFBOHeight - 1, 199, 40, 0, 255, 1.0);
+}
+
+// Test resolving a multisampled texture with blit. Draw non-flipped, resolve with draw fbo flipped
+TEST_P(BlitFramebufferTestES31, MultisampleFlippedResolveDrawWithBlitAndNonFlippedDraw)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ // Draw non-flipped - explicitly set y-flip to 0.
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture;
+ GLFramebuffer resolveFBO;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture);
+ constexpr int kResolveFBOWidth = kSize - 3;
+ constexpr int kResolveFBOHeight = kSize - 2;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kResolveFBOWidth, kResolveFBOHeight, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ // Resolve with draw fbo flipped and read fbo non-flipped.
+ glFramebufferParameteriMESA(GL_READ_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+ glFramebufferParameteriMESA(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glBlitFramebuffer(0, 0, kResolveFBOWidth, kResolveFBOHeight, 0, 0, kResolveFBOWidth,
+ kResolveFBOHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ constexpr uint8_t kHalfPixelGradient = 256 / kSize / 2;
+ EXPECT_PIXEL_NEAR(0, 0, kHalfPixelGradient, kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, 0, 199, kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(0, kResolveFBOHeight - 1, kHalfPixelGradient, 215, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, kResolveFBOHeight - 1, 199, 215, 0, 255, 1.0);
+}
+
+// Test resolving a multisampled texture with blit. Draw non-flipped, resolve with both read and
+// draw fbos flipped
+TEST_P(BlitFramebufferTestES31, MultisampleFlippedResolveWithBlitAndNonFlippedDraw)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ // Draw non-flipped - explicitly set y-flip to 0.
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture;
+ GLFramebuffer resolveFBO;
+ constexpr int kResolveFBOWidth = kSize - 3;
+ constexpr int kResolveFBOHeight = kSize - 2;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kResolveFBOWidth, kResolveFBOHeight, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ // Resolve with draw and read fbo flipped.
+ glFramebufferParameteriMESA(GL_READ_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glFramebufferParameteriMESA(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glBlitFramebuffer(0, 0, kResolveFBOWidth, kResolveFBOHeight, 0, 0, kResolveFBOWidth,
+ kResolveFBOHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ constexpr uint8_t kHalfPixelGradient = 256 / kSize / 2;
+ EXPECT_PIXEL_NEAR(0, 0, kHalfPixelGradient, 255 - kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, 0, 199, 255 - kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(0, kResolveFBOHeight - 1, kHalfPixelGradient, 40, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kResolveFBOWidth - 1, kResolveFBOHeight - 1, 199, 40, 0, 255, 1.0);
+}
+
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BlitFramebufferANGLETest);
diff --git a/src/tests/gl_tests/CopyTexImageTest.cpp b/src/tests/gl_tests/CopyTexImageTest.cpp
index 10c6313..951f642 100644
--- a/src/tests/gl_tests/CopyTexImageTest.cpp
+++ b/src/tests/gl_tests/CopyTexImageTest.cpp
@@ -90,6 +90,33 @@
EXPECT_PIXEL_NEAR((xs + xe) / 2, (ys + ye) / 2, data[0], data[1], data[2], data[3], 1.0);
}
+ void verifyCheckeredResults(GLuint texture,
+ const GLubyte data0[4],
+ const GLubyte data1[4],
+ const GLubyte data2[4],
+ const GLubyte data3[4],
+ GLint fboWidth,
+ GLint fboHeight)
+ {
+ glViewport(0, 0, fboWidth, fboHeight);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // Draw a quad with the target texture
+ glUseProgram(mTextureProgram);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glUniform1i(mTextureUniformLocation, 0);
+
+ drawQuad(mTextureProgram, essl1_shaders::PositionAttrib(), 0.5f);
+
+ // Expect that the rendered quad has the same color as the source texture
+ EXPECT_PIXEL_EQ(fboWidth / 4, fboHeight / 4, data0[0], data0[1], data0[2], data0[3]);
+ EXPECT_PIXEL_EQ(fboWidth / 4, 3 * fboHeight / 4, data1[0], data1[1], data1[2], data1[3]);
+ EXPECT_PIXEL_EQ(3 * fboWidth / 4, fboHeight / 4, data2[0], data2[1], data2[2], data2[3]);
+ EXPECT_PIXEL_EQ(3 * fboWidth / 4, 3 * fboHeight / 4, data3[0], data3[1], data3[2],
+ data3[3]);
+ }
+
void runCopyTexImageTest(GLenum format, GLubyte expected[3][4])
{
GLTexture tex;
@@ -116,6 +143,47 @@
}
}
+ // x, y, width, height specify the portion of fbo to be copied into tex.
+ // flip_y specifies if the glCopyTextImage must be done from y-flipped fbo.
+ void runCopyTexImageTestCheckered(GLenum format,
+ const uint32_t x[3],
+ const uint32_t y[3],
+ const uint32_t width[3],
+ const uint32_t height[3],
+ const GLubyte expectedData0[4],
+ const GLubyte expectedData1[4],
+ const GLubyte expectedData2[4],
+ const GLubyte expectedData3[4],
+ bool mesaFlipY)
+ {
+ GLTexture tex;
+ glBindTexture(GL_TEXTURE_2D, tex);
+
+ // Disable mipmapping
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ // Perform the copy multiple times.
+ for (size_t i = 0; i < kFboCount; ++i)
+ {
+ glViewport(0, 0, kFboSizes[i], kFboSizes[i]);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFbos[i]);
+
+ ANGLE_GL_PROGRAM(checkerProgram, essl1_shaders::vs::Passthrough(),
+ essl1_shaders::fs::Checkered());
+ drawQuad(checkerProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
+ EXPECT_GL_NO_ERROR();
+
+ if (mesaFlipY)
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x[i], y[i], width[i], height[i], 0);
+ ASSERT_GL_NO_ERROR();
+
+ verifyCheckeredResults(tex, expectedData0, expectedData1, expectedData2, expectedData3,
+ kFboSizes[i], kFboSizes[i]);
+ }
+ }
+
void runCopyTexSubImageTest(GLenum format, GLubyte expected[3][4])
{
GLTexture tex;
@@ -566,6 +634,107 @@
}
}
+// Tests image copy from y-flipped fbo works.
+TEST_P(CopyTexImageTest, CopyTexImageMesaYFlip)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ std::array<uint32_t, 3> copyOrigin{0};
+ std::array<uint32_t, 3> copySize;
+ for (size_t i = 0; i < kFboCount; i++)
+ copySize[i] = kFboSizes[i];
+
+ initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
+ runCopyTexImageTestCheckered(GL_RGBA, copyOrigin.data(), copyOrigin.data(), copySize.data(),
+ copySize.data(), GLColor::green.data(), GLColor::red.data(),
+ GLColor::yellow.data(), GLColor::blue.data(),
+ true /* mesaFlipY */);
+}
+
+// Tests image partial copy from y-flipped fbo works.
+TEST_P(CopyTexImageTest, CopyTexImageMesaYFlipPartial)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ std::array<uint32_t, kFboCount> copyX;
+ std::array<uint32_t, kFboCount> copyY{0};
+ std::array<uint32_t, kFboCount> copyWidth;
+ std::array<uint32_t, kFboCount> copyHeight;
+
+ for (size_t i = 0; i < kFboCount; i++)
+ {
+ copyX[i] = kFboSizes[i] / 2;
+ copyHeight[i] = kFboSizes[i];
+ }
+ copyWidth = copyX;
+
+ initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
+ runCopyTexImageTestCheckered(GL_RGBA, copyX.data(), copyY.data(), copyWidth.data(),
+ copyHeight.data(), GLColor::yellow.data(), GLColor::blue.data(),
+ GLColor::yellow.data(), GLColor::blue.data(),
+ true /* mesaFlipY */);
+}
+
+// Tests subimage copy from y-flipped fbo works.
+TEST_P(CopyTexImageTest, CopyTexSubImageMesaYFlip)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ GLuint format = GL_RGBA;
+ initializeResources(format, GL_UNSIGNED_BYTE);
+
+ glViewport(0, 0, kFboSizes[0], kFboSizes[0]);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFbos[0]);
+
+ ANGLE_GL_PROGRAM(checkerProgram, essl1_shaders::vs::Passthrough(),
+ essl1_shaders::fs::Checkered());
+ drawQuad(checkerProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
+ EXPECT_GL_NO_ERROR();
+
+ GLTexture tex;
+ glBindTexture(GL_TEXTURE_2D, tex);
+
+ // Disable mipmapping
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ // Create the texture with copy of the first fbo.
+ glBindFramebuffer(GL_FRAMEBUFFER, mFbos[0]);
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, kFboSizes[0], kFboSizes[0], 0);
+ ASSERT_GL_NO_ERROR();
+
+ // Make sure out-of-bound writes to the texture return invalid value.
+ glBindFramebuffer(GL_FRAMEBUFFER, mFbos[1]);
+ drawQuad(checkerProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
+ EXPECT_GL_NO_ERROR();
+
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ // xoffset < 0 and yoffset < 0
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, -1, -1, 0, 0, kFboSizes[0], kFboSizes[0]);
+ ASSERT_GL_ERROR(GL_INVALID_VALUE);
+
+ // xoffset + width > w and yoffset + height > h
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 0, 0, kFboSizes[0], kFboSizes[0]);
+ ASSERT_GL_ERROR(GL_INVALID_VALUE);
+
+ // xoffset + width > w and yoffset + height > h, out of bounds
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, -1, -1, 1 + kFboSizes[0], 1 + kFboSizes[0]);
+ ASSERT_GL_ERROR(GL_INVALID_VALUE);
+
+ // Copy the second fbo over a portion of the image.
+ GLint offset = kFboSizes[0] / 2;
+ GLint extent = kFboSizes[0] - offset;
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, offset, offset, kFboSizes[1] / 2, kFboSizes[1] / 2,
+ extent, extent);
+ ASSERT_GL_NO_ERROR();
+
+ // Only part of the image is changed.
+ verifyCheckeredResults(tex, GLColor::green.data(), GLColor::red.data(), GLColor::yellow.data(),
+ GLColor::blue.data(), kFboSizes[0], kFboSizes[0]);
+}
+
// specialization of CopyTexImageTest is added so that some tests can be explicitly run with an ES3
// context
class CopyTexImageTestES3 : public CopyTexImageTest
diff --git a/src/tests/gl_tests/CopyTextureTest.cpp b/src/tests/gl_tests/CopyTextureTest.cpp
index ab4fac8..8534c16 100644
--- a/src/tests/gl_tests/CopyTextureTest.cpp
+++ b/src/tests/gl_tests/CopyTextureTest.cpp
@@ -115,7 +115,7 @@
};
using CopyTextureVariationsTestParams =
- std::tuple<angle::PlatformParameters, GLenum, GLenum, bool, bool, bool>;
+ std::tuple<angle::PlatformParameters, GLenum, GLenum, bool, bool, bool, GLint>;
std::string CopyTextureVariationsTestPrint(
const ::testing::TestParamInfo<CopyTextureVariationsTestParams> ¶msInfo)
@@ -184,6 +184,10 @@
{
out << "UnmultiplyAlpha";
}
+ if (std::get<6>(params))
+ {
+ out << "MesaYFlip";
+ }
return out.str();
}
@@ -422,13 +426,18 @@
GLenum destFormat,
bool flipY,
bool premultiplyAlpha,
- bool unmultiplyAlpha)
+ bool unmultiplyAlpha,
+ GLint mesaYFlipParam)
{
if (!checkExtensions(sourceFormat, destFormat))
{
return;
}
+ const bool hasMesaFbFlipYExt = IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y");
+ if (mesaYFlipParam && !hasMesaFbFlipYExt)
+ ASSERT_TRUE(hasMesaFbFlipYExt);
+
if (sourceFormat == GL_LUMINANCE || sourceFormat == GL_LUMINANCE_ALPHA ||
sourceFormat == GL_ALPHA || destFormat == GL_LUMINANCE ||
destFormat == GL_LUMINANCE_ALPHA || destFormat == GL_ALPHA)
@@ -457,6 +466,11 @@
initializeSourceTexture(sourceTarget, sourceFormat, &srcColors[i * componentCount],
componentCount);
+ if (hasMesaFbFlipYExt)
+ {
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA,
+ mesaYFlipParam);
+ }
glCopyTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, destFormat,
GL_UNSIGNED_BYTE, flipY, premultiplyAlpha, unmultiplyAlpha);
@@ -467,17 +481,37 @@
if (flipY)
{
- EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
+ if (mesaYFlipParam)
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
+ }
+ else
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
+ }
}
else
{
- EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
+ if (mesaYFlipParam)
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
+ }
+ else
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
+ }
}
EXPECT_GL_NO_ERROR();
@@ -489,13 +523,18 @@
GLenum destFormat,
bool flipY,
bool premultiplyAlpha,
- bool unmultiplyAlpha)
+ bool unmultiplyAlpha,
+ GLint mesaYFlipParam)
{
if (!checkExtensions(sourceFormat, destFormat))
{
return;
}
+ const bool hasMesaFbFlipYExt = IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y");
+ if (mesaYFlipParam && !hasMesaFbFlipYExt)
+ ASSERT_TRUE(hasMesaFbFlipYExt);
+
if (sourceFormat == GL_LUMINANCE || sourceFormat == GL_LUMINANCE_ALPHA ||
sourceFormat == GL_ALPHA || destFormat == GL_LUMINANCE ||
destFormat == GL_LUMINANCE_ALPHA || destFormat == GL_ALPHA)
@@ -528,6 +567,11 @@
glTexImage2D(GL_TEXTURE_2D, 0, destFormat, 2, 2, 0, destFormat, GL_UNSIGNED_BYTE,
nullptr);
+ if (hasMesaFbFlipYExt)
+ {
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA,
+ mesaYFlipParam);
+ }
glCopySubTextureCHROMIUM(mTextures[0], 0, GL_TEXTURE_2D, mTextures[1], 0, 0, 0, 0, 0, 2,
2, flipY, premultiplyAlpha, unmultiplyAlpha);
@@ -542,17 +586,37 @@
if (flipY)
{
- EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
+ if (mesaYFlipParam)
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
+ }
+ else
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
+ }
}
else
{
- EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
- EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
+ if (mesaYFlipParam)
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 3], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 1], 1.0);
+ }
+ else
+ {
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, destColors[i + 0], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 0, destColors[i + 1], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(0, 1, destColors[i + 2], 1.0);
+ EXPECT_PIXEL_COLOR_NEAR(1, 1, destColors[i + 3], 1.0);
+ }
}
EXPECT_GL_NO_ERROR();
@@ -904,6 +968,7 @@
GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_BGRA_EXT};
constexpr GLenum kCopyTextureVariationsDstFormats[] = {GL_RGB, GL_RGBA, GL_BGRA_EXT,
GL_SRGB_ALPHA_EXT};
+constexpr GLint kMesaYFlips[] = {0, 1};
} // anonymous namespace
TEST_P(CopyTextureVariationsTest, CopyTexture)
@@ -917,32 +982,57 @@
ANGLE_SKIP_TEST_IF(IsWindows7() && IsNVIDIA() && IsOpenGLES());
}
+ if (std::get<6>(GetParam()))
+ {
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+ }
+
testCopyTexture(GL_TEXTURE_2D, std::get<1>(GetParam()), std::get<2>(GetParam()),
- std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()));
+ std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()),
+ std::get<6>(GetParam()));
}
TEST_P(CopyTextureVariationsTest, CopySubTexture)
{
// http://anglebug.com/5723
ANGLE_SKIP_TEST_IF(IsOzone());
+
+ if (std::get<6>(GetParam()))
+ {
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+ }
+
testCopySubTexture(GL_TEXTURE_2D, std::get<1>(GetParam()), std::get<2>(GetParam()),
- std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()));
+ std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()),
+ std::get<6>(GetParam()));
}
TEST_P(CopyTextureVariationsTest, CopyTextureRectangle)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_texture_rectangle"));
+ if (std::get<6>(GetParam()))
+ {
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+ }
+
testCopyTexture(GL_TEXTURE_RECTANGLE_ANGLE, std::get<1>(GetParam()), std::get<2>(GetParam()),
- std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()));
+ std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()),
+ std::get<6>(GetParam()));
}
TEST_P(CopyTextureVariationsTest, CopySubTextureRectangle)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_texture_rectangle"));
+ if (std::get<6>(GetParam()))
+ {
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+ }
+
testCopySubTexture(GL_TEXTURE_RECTANGLE_ANGLE, std::get<1>(GetParam()), std::get<2>(GetParam()),
- std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()));
+ std::get<3>(GetParam()), std::get<4>(GetParam()), std::get<5>(GetParam()),
+ std::get<6>(GetParam()));
}
// Test that copying to cube maps works
@@ -2534,13 +2624,14 @@
#endif
ANGLE_INSTANTIATE_TEST_ES2(CopyTextureTest);
-ANGLE_INSTANTIATE_TEST_COMBINE_5(CopyTextureVariationsTest,
+ANGLE_INSTANTIATE_TEST_COMBINE_6(CopyTextureVariationsTest,
CopyTextureVariationsTestPrint,
testing::ValuesIn(kCopyTextureVariationsSrcFormats),
testing::ValuesIn(kCopyTextureVariationsDstFormats),
testing::Bool(), // flipY
testing::Bool(), // premultiplyAlpha
testing::Bool(), // unmultiplyAlpha
+ testing::ValuesIn(kMesaYFlips),
ES2_D3D9(),
ES2_D3D11(),
ES2_OPENGL(),
diff --git a/src/tests/gl_tests/FramebufferTest.cpp b/src/tests/gl_tests/FramebufferTest.cpp
index 9e71e2d..1d82fd6 100644
--- a/src/tests/gl_tests/FramebufferTest.cpp
+++ b/src/tests/gl_tests/FramebufferTest.cpp
@@ -1863,6 +1863,43 @@
ASSERT_GL_NO_ERROR();
}
+// Tests that draw to Y-flipped FBO results in correct pixels.
+TEST_P(FramebufferTest_ES31, BasicDrawToYFlippedFBO)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
+
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
+
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D, texture.get());
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Remove the flag so that glReadPixels do not implicitly use that.
+ glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 0);
+
+ constexpr uint8_t kHalfPixelGradient = 256 / kSize / 2;
+ EXPECT_PIXEL_NEAR(0, 0, kHalfPixelGradient, 255 - kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kSize - 1, 0, 255 - kHalfPixelGradient, 255 - kHalfPixelGradient, 0, 255,
+ 1.0);
+ EXPECT_PIXEL_NEAR(0, kSize - 1, kHalfPixelGradient, kHalfPixelGradient, 0, 255, 1.0);
+ EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 255 - kHalfPixelGradient, kHalfPixelGradient, 0, 255,
+ 1.0);
+}
+
// Test resolving a multisampled texture with blit
TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlit)
{
diff --git a/src/tests/test_utils/angle_test_instantiate.h b/src/tests/test_utils/angle_test_instantiate.h
index 9437d17..abf8f5a 100644
--- a/src/tests/test_utils/angle_test_instantiate.h
+++ b/src/tests/test_utils/angle_test_instantiate.h
@@ -256,6 +256,14 @@
testing::Combine(ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
combine1, combine2, combine3, combine4, combine5), \
print)
+#define ANGLE_INSTANTIATE_TEST_COMBINE_6(testName, print, combine1, combine2, combine3, combine4, \
+ combine5, combine6, first, ...) \
+ const decltype(first) testName##params[] = {first, ##__VA_ARGS__}; \
+ INSTANTIATE_TEST_SUITE_P( \
+ , testName, \
+ testing::Combine(ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), combine1, combine2, combine3, \
+ combine4, combine5, combine6), \
+ print)
// Checks if a config is expected to be supported by checking a system-based allow list.
bool IsConfigAllowlisted(const SystemInfo &systemInfo, const PlatformParameters ¶m);