diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp index 80af23363..e6e6b3cd6 100644 --- a/src/libANGLE/renderer/gl/renderergl_utils.cpp +++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp @@ -1801,13 +1801,8 @@ void GenerateCaps(const FunctionsGL *functions, 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 4abb7d81b..06afbf8a1 100644 --- a/src/tests/gl_tests/BlitFramebufferANGLETest.cpp +++ b/src/tests/gl_tests/BlitFramebufferANGLETest.cpp @@ -350,6 +350,67 @@ class BlitFramebufferANGLETest : public ANGLETest 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 @@ TEST_P(BlitFramebufferANGLETest, BlitColorWithFlip) 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 @@ TEST_P(BlitFramebufferANGLETest, BlitStencil) // 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 @@ class BlitFramebufferTest : public ANGLETest 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_P(BlitFramebufferTest, OOBWrite) // 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 @@ TEST_P(BlitFramebufferTestES31, BlitMultisampledRGBX8ToRGB8) 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 10c6313a6..951f64213 100644 --- a/src/tests/gl_tests/CopyTexImageTest.cpp +++ b/src/tests/gl_tests/CopyTexImageTest.cpp @@ -90,6 +90,33 @@ class CopyTexImageTest : public ANGLETest 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 @@ class CopyTexImageTest : public ANGLETest } } + // 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 @@ TEST_P(CopyTexImageTest, CopyTexSubImageFrom3DTexureOES) } } +// Tests image copy from y-flipped fbo works. +TEST_P(CopyTexImageTest, CopyTexImageMesaYFlip) +{ + ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y")); + + std::array copyOrigin{0}; + std::array 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 copyX; + std::array copyY{0}; + std::array copyWidth; + std::array 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 ab4fac8f2..8534c1635 100644 --- a/src/tests/gl_tests/CopyTextureTest.cpp +++ b/src/tests/gl_tests/CopyTextureTest.cpp @@ -115,7 +115,7 @@ class CopyTextureTest : public ANGLETest }; using CopyTextureVariationsTestParams = - std::tuple; + std::tuple; std::string CopyTextureVariationsTestPrint( const ::testing::TestParamInfo ¶msInfo) @@ -184,6 +184,10 @@ std::string CopyTextureVariationsTestPrint( { out << "UnmultiplyAlpha"; } + if (std::get<6>(params)) + { + out << "MesaYFlip"; + } return out.str(); } @@ -422,13 +426,18 @@ class CopyTextureVariationsTest : public ANGLETestWithParam(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 @@ TEST_P(CopyTextureTestES3, InvalidateBlitThenBlend1000Layers) #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 9e71e2d6f..1d82fd6c2 100644 --- a/src/tests/gl_tests/FramebufferTest.cpp +++ b/src/tests/gl_tests/FramebufferTest.cpp @@ -1863,6 +1863,43 @@ TEST_P(FramebufferTest_ES31, IncompleteMultisampleFixedSampleLocationsTex) 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 9437d17ff..abf8f5aa9 100644 --- a/src/tests/test_utils/angle_test_instantiate.h +++ b/src/tests/test_utils/angle_test_instantiate.h @@ -256,6 +256,14 @@ struct CombinedPrintToStringParamName 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);