blob: 348db863ee6d2121175d191f160179dedb0e8842 [file] [log] [blame]
//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
class BlitFramebufferANGLETest : public ANGLETest<>
{
protected:
BlitFramebufferANGLETest()
{
setWindowWidth(64);
setWindowHeight(32);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
setConfigStencilBits(8);
mCheckerProgram = 0;
mBlueProgram = 0;
mRedProgram = 0;
mOriginalFBO = 0;
mUserFBO = 0;
mUserColorBuffer = 0;
mUserDepthStencilBuffer = 0;
mSmallFBO = 0;
mSmallColorBuffer = 0;
mSmallDepthStencilBuffer = 0;
mColorOnlyFBO = 0;
mColorOnlyColorBuffer = 0;
mDiffFormatFBO = 0;
mDiffFormatColorBuffer = 0;
mDiffSizeFBO = 0;
mDiffSizeColorBuffer = 0;
mMRTFBO = 0;
mMRTColorBuffer0 = 0;
mMRTColorBuffer1 = 0;
mRGBAColorbuffer = 0;
mRGBAFBO = 0;
mRGBAMultisampledRenderbuffer = 0;
mRGBAMultisampledFBO = 0;
mBGRAColorbuffer = 0;
mBGRAFBO = 0;
mBGRAMultisampledRenderbuffer = 0;
mBGRAMultisampledFBO = 0;
}
void testSetUp() override
{
mCheckerProgram =
CompileProgram(essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Checkered());
mBlueProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
mRedProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
if (mCheckerProgram == 0 || mBlueProgram == 0 || mRedProgram == 0)
{
FAIL() << "shader compilation failed.";
}
EXPECT_GL_NO_ERROR();
GLint originalFBO;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &originalFBO);
if (originalFBO >= 0)
{
mOriginalFBO = (GLuint)originalFBO;
}
GLenum format = GL_RGBA;
glGenFramebuffers(1, &mUserFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glGenTextures(1, &mUserColorBuffer);
glGenRenderbuffers(1, &mUserDepthStencilBuffer);
glBindTexture(GL_TEXTURE_2D, mUserColorBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mUserColorBuffer, 0);
glTexImage2D(GL_TEXTURE_2D, 0, format, getWindowWidth(), getWindowHeight(), 0, format,
GL_UNSIGNED_BYTE, nullptr);
glBindRenderbuffer(GL_RENDERBUFFER, mUserDepthStencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, getWindowWidth(),
getWindowHeight());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
mUserDepthStencilBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
mUserDepthStencilBuffer);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
glGenFramebuffers(1, &mSmallFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mSmallFBO);
glGenTextures(1, &mSmallColorBuffer);
glGenRenderbuffers(1, &mSmallDepthStencilBuffer);
glBindTexture(GL_TEXTURE_2D, mSmallColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, format, getWindowWidth() / 2, getWindowHeight() / 2, 0,
format, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mSmallColorBuffer, 0);
glBindRenderbuffer(GL_RENDERBUFFER, mSmallDepthStencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, getWindowWidth() / 2,
getWindowHeight() / 2);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
mSmallDepthStencilBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
mSmallDepthStencilBuffer);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
glGenFramebuffers(1, &mColorOnlyFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mColorOnlyFBO);
glGenTextures(1, &mColorOnlyColorBuffer);
glBindTexture(GL_TEXTURE_2D, mColorOnlyColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, format, getWindowWidth(), getWindowHeight(), 0, format,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mColorOnlyColorBuffer, 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
glGenFramebuffers(1, &mDiffFormatFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mDiffFormatFBO);
glGenTextures(1, &mDiffFormatColorBuffer);
glBindTexture(GL_TEXTURE_2D, mDiffFormatColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mDiffFormatColorBuffer, 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
glGenFramebuffers(1, &mDiffSizeFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mDiffSizeFBO);
glGenTextures(1, &mDiffSizeColorBuffer);
glBindTexture(GL_TEXTURE_2D, mDiffSizeColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, format, getWindowWidth() * 2, getWindowHeight() * 2, 0,
format, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mDiffSizeColorBuffer, 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
if (IsGLExtensionEnabled("GL_EXT_draw_buffers"))
{
glGenFramebuffers(1, &mMRTFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mMRTFBO);
glGenTextures(1, &mMRTColorBuffer0);
glGenTextures(1, &mMRTColorBuffer1);
glBindTexture(GL_TEXTURE_2D, mMRTColorBuffer0);
glTexImage2D(GL_TEXTURE_2D, 0, format, getWindowWidth(), getWindowHeight(), 0, format,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
mMRTColorBuffer0, 0);
glBindTexture(GL_TEXTURE_2D, mMRTColorBuffer1);
glTexImage2D(GL_TEXTURE_2D, 0, format, getWindowWidth(), getWindowHeight(), 0, format,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
mMRTColorBuffer1, 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
if (IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample") &&
IsGLExtensionEnabled("GL_OES_rgb8_rgba8"))
{
// RGBA single-sampled framebuffer
glGenTextures(1, &mRGBAColorbuffer);
glBindTexture(GL_TEXTURE_2D, mRGBAColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glGenFramebuffers(1, &mRGBAFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mRGBAFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mRGBAColorbuffer, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// RGBA multisampled framebuffer
glGenRenderbuffers(1, &mRGBAMultisampledRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRGBAMultisampledRenderbuffer);
glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 1, GL_RGBA8, getWindowWidth(),
getWindowHeight());
glGenFramebuffers(1, &mRGBAMultisampledFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mRGBAMultisampledFBO);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
mRGBAMultisampledRenderbuffer);
ASSERT_GL_NO_ERROR();
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888"))
{
// BGRA single-sampled framebuffer
glGenTextures(1, &mBGRAColorbuffer);
glBindTexture(GL_TEXTURE_2D, mBGRAColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, getWindowWidth(), getWindowHeight(), 0,
GL_BGRA_EXT, GL_UNSIGNED_BYTE, nullptr);
glGenFramebuffers(1, &mBGRAFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mBGRAFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mBGRAColorbuffer, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// BGRA multisampled framebuffer
glGenRenderbuffers(1, &mBGRAMultisampledRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mBGRAMultisampledRenderbuffer);
glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 1, GL_BGRA8_EXT,
getWindowWidth(), getWindowHeight());
glGenFramebuffers(1, &mBGRAMultisampledFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mBGRAMultisampledFBO);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
mBGRAMultisampledRenderbuffer);
ASSERT_GL_NO_ERROR();
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
}
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
}
void testTearDown() override
{
glDeleteProgram(mCheckerProgram);
glDeleteProgram(mBlueProgram);
glDeleteProgram(mRedProgram);
glDeleteFramebuffers(1, &mUserFBO);
glDeleteTextures(1, &mUserColorBuffer);
glDeleteRenderbuffers(1, &mUserDepthStencilBuffer);
glDeleteFramebuffers(1, &mSmallFBO);
glDeleteTextures(1, &mSmallColorBuffer);
glDeleteRenderbuffers(1, &mSmallDepthStencilBuffer);
glDeleteFramebuffers(1, &mColorOnlyFBO);
glDeleteTextures(1, &mSmallDepthStencilBuffer);
glDeleteFramebuffers(1, &mDiffFormatFBO);
glDeleteTextures(1, &mDiffFormatColorBuffer);
glDeleteFramebuffers(1, &mDiffSizeFBO);
glDeleteTextures(1, &mDiffSizeColorBuffer);
if (IsGLExtensionEnabled("GL_EXT_draw_buffers"))
{
glDeleteFramebuffers(1, &mMRTFBO);
glDeleteTextures(1, &mMRTColorBuffer0);
glDeleteTextures(1, &mMRTColorBuffer1);
}
if (mRGBAColorbuffer != 0)
{
glDeleteTextures(1, &mRGBAColorbuffer);
}
if (mRGBAFBO != 0)
{
glDeleteFramebuffers(1, &mRGBAFBO);
}
if (mRGBAMultisampledRenderbuffer != 0)
{
glDeleteRenderbuffers(1, &mRGBAMultisampledRenderbuffer);
}
if (mRGBAMultisampledFBO != 0)
{
glDeleteFramebuffers(1, &mRGBAMultisampledFBO);
}
if (mBGRAColorbuffer != 0)
{
glDeleteTextures(1, &mBGRAColorbuffer);
}
if (mBGRAFBO != 0)
{
glDeleteFramebuffers(1, &mBGRAFBO);
}
if (mBGRAMultisampledRenderbuffer != 0)
{
glDeleteRenderbuffers(1, &mBGRAMultisampledRenderbuffer);
}
if (mBGRAMultisampledFBO != 0)
{
glDeleteFramebuffers(1, &mBGRAMultisampledFBO);
}
}
void multisampleTestHelper(GLuint readFramebuffer, GLuint drawFramebuffer)
{
glClearColor(0.0, 1.0, 0.0, 1.0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, readFramebuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFramebuffer);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, drawFramebuffer);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255);
EXPECT_PIXEL_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255);
EXPECT_PIXEL_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, 0, 255, 0, 255);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, 0, 255, 0, 255);
}
bool checkExtension(const std::string &extension)
{
if (!IsGLExtensionEnabled(extension))
{
std::cout << "Test skipped because " << extension << " not supported." << std::endl;
return false;
}
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;
GLuint mOriginalFBO;
GLuint mUserFBO;
GLuint mUserColorBuffer;
GLuint mUserDepthStencilBuffer;
GLuint mSmallFBO;
GLuint mSmallColorBuffer;
GLuint mSmallDepthStencilBuffer;
GLuint mColorOnlyFBO;
GLuint mColorOnlyColorBuffer;
GLuint mDiffFormatFBO;
GLuint mDiffFormatColorBuffer;
GLuint mDiffSizeFBO;
GLuint mDiffSizeColorBuffer;
GLuint mMRTFBO;
GLuint mMRTColorBuffer0;
GLuint mMRTColorBuffer1;
GLuint mRGBAColorbuffer;
GLuint mRGBAFBO;
GLuint mRGBAMultisampledRenderbuffer;
GLuint mRGBAMultisampledFBO;
GLuint mBGRAColorbuffer;
GLuint mBGRAFBO;
GLuint mBGRAMultisampledRenderbuffer;
GLuint mBGRAMultisampledFBO;
};
// Draw to user-created framebuffer, blit whole-buffer color to original framebuffer.
TEST_P(BlitFramebufferANGLETest, BlitColorToDefault)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
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();
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_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::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
}
// Blit color to/from default framebuffer with Flip-X/Flip-Y.
TEST_P(BlitFramebufferANGLETest, BlitColorWithFlip)
{
// OpenGL ES 3.0 / GL_NV_framebuffer_blit required for flip.
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 &&
!IsGLExtensionEnabled("GL_NV_framebuffer_blit"));
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 with x-flip.
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), getWindowWidth(), 0, 0,
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::green);
// Blit to default with y-flip.
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, getWindowHeight(),
getWindowWidth(), 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::blue);
// Blit from default with x-flip.
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), getWindowWidth(), 0, 0,
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::red);
// Blit from default with y-flip.
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, getWindowHeight(),
getWindowWidth(), 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
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::blue);
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)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
// TODO(jmadill): Fix this. http://anglebug.com/42261451
ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
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();
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mUserFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
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::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
}
// blit from user-created FBO to system framebuffer, with the scissor test enabled.
TEST_P(BlitFramebufferANGLETest, ScissoredBlit)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
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();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glScissor(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight());
glEnable(GL_SCISSOR_TEST);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
}
// blit from system FBO to user-created framebuffer, with the scissor test enabled.
TEST_P(BlitFramebufferANGLETest, ReverseScissoredBlit)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
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();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mUserFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mOriginalFBO);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glScissor(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight());
glEnable(GL_SCISSOR_TEST);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
}
// blit from user-created FBO to system framebuffer, using region larger than buffer.
TEST_P(BlitFramebufferANGLETest, OversizedBlit)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
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();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBlitFramebufferANGLE(0, 0, getWindowWidth() * 2, getWindowHeight() * 2, 0, 0,
getWindowWidth() * 2, getWindowHeight() * 2, GL_COLOR_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::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
}
// blit from system FBO to user-created framebuffer, using region larger than buffer.
TEST_P(BlitFramebufferANGLETest, ReverseOversizedBlit)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
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();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mUserFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mOriginalFBO);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBlitFramebufferANGLE(0, 0, getWindowWidth() * 2, getWindowHeight() * 2, 0, 0,
getWindowWidth() * 2, getWindowHeight() * 2, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
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::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
}
// blit from user-created FBO to system framebuffer, with depth buffer.
TEST_P(BlitFramebufferANGLETest, BlitWithDepthUserToDefault)
{
// TODO(http://anglebug.com/42264679): glBlitFramebufferANGLE() generates GL_INVALID_OPERATION
// for the ES2_OpenGL backend.
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL());
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
EXPECT_GL_NO_ERROR();
// Clear the first half of the screen
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, getWindowWidth(), getWindowHeight() / 2);
glClearDepthf(0.1f);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Scissor the second half of the screen
glScissor(0, getWindowHeight() / 2, getWindowWidth(), getWindowHeight() / 2);
glClearDepthf(0.9f);
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
// if blit is happening correctly, this quad will draw only on the bottom half since it will
// be behind on the first half and in front on the second half.
drawQuad(mBlueProgram, essl1_shaders::PositionAttrib(), 0.5f);
glDisable(GL_DEPTH_TEST);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::blue);
}
// blit from system FBO to user-created framebuffer, with depth buffer.
TEST_P(BlitFramebufferANGLETest, BlitWithDepthDefaultToUser)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
EXPECT_GL_NO_ERROR();
// Clear the first half of the screen
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, getWindowWidth(), getWindowHeight() / 2);
glClearDepthf(0.1f);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Scissor the second half of the screen
glScissor(0, getWindowHeight() / 2, getWindowWidth(), getWindowHeight() / 2);
glClearDepthf(0.9f);
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mUserFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
// if blit is happening correctly, this quad will draw only on the bottom half since it will be
// behind on the first half and in front on the second half.
drawQuad(mBlueProgram, essl1_shaders::PositionAttrib(), 0.5f);
glDisable(GL_DEPTH_TEST);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::blue);
}
// blit from one region of the system fbo to another-- this should fail.
TEST_P(BlitFramebufferANGLETest, BlitSameBufferOriginal)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.3f);
EXPECT_GL_NO_ERROR();
glBlitFramebufferANGLE(0, 0, getWindowWidth() / 2, getWindowHeight(), getWindowWidth() / 2, 0,
getWindowWidth(), getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// blit from one region of the system fbo to another.
TEST_P(BlitFramebufferANGLETest, BlitSameBufferUser)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.3f);
EXPECT_GL_NO_ERROR();
glBlitFramebufferANGLE(0, 0, getWindowWidth() / 2, getWindowHeight(), getWindowWidth() / 2, 0,
getWindowWidth(), getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
TEST_P(BlitFramebufferANGLETest, BlitPartialColor)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBlitFramebufferANGLE(0, 0, getWindowWidth() / 2, getWindowHeight() / 2, 0,
getWindowHeight() / 2, getWindowWidth() / 2, getWindowHeight(),
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mOriginalFBO);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, getWindowHeight() / 4, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::white);
}
TEST_P(BlitFramebufferANGLETest, BlitDifferentSizes)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mSmallFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glClearColor(1.0, 1.0, 1.0, 1.0);
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_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mSmallFBO);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::red);
EXPECT_GL_NO_ERROR();
}
// Test that blit with missing attachments is ignored.
TEST_P(BlitFramebufferANGLETest, BlitWithMissingAttachments)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mColorOnlyFBO);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.3f);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mColorOnlyFBO);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// No error if the read FBO has no depth attachment
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
// No error if the read FBO has no stencil attachment
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
// No error if we read from a missing color attachment. Create a temp attachment as
// attachment1, then remove attachment 0.
//
// The same could be done with glReadBuffer, which requires ES3 (this test runs on ES2).
GLTexture tempColor;
glBindTexture(GL_TEXTURE_2D, tempColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tempColor, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_READ_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
}
TEST_P(BlitFramebufferANGLETest, BlitStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
// http://anglebug.com/40096473
ANGLE_SKIP_TEST_IF(IsIntel() && IsD3D9());
// http://anglebug.com/42263934
ANGLE_SKIP_TEST_IF(IsAMD() && IsD3D9());
BlitStencilTestHelper(false /* mesaFlipY */);
}
// 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"));
// http://anglebug.com/40096473
ANGLE_SKIP_TEST_IF(IsIntel() && IsD3D9());
// http://anglebug.com/42263934
ANGLE_SKIP_TEST_IF(IsAMD() && IsD3D9());
BlitStencilTestHelper(true /* mesaFlipY */);
}
// make sure that attempting to blit a partial depth buffer issues an error
TEST_P(BlitFramebufferANGLETest, BlitPartialDepthStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth() / 2, getWindowHeight() / 2, 0, 0,
getWindowWidth() / 2, getWindowHeight() / 2, GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Test blit with MRT framebuffers
TEST_P(BlitFramebufferANGLETest, BlitMRT)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
if (!IsGLExtensionEnabled("GL_EXT_draw_buffers"))
{
return;
}
GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
glBindFramebuffer(GL_FRAMEBUFFER, mMRTFBO);
glDrawBuffersEXT(2, drawBuffers);
glBindFramebuffer(GL_FRAMEBUFFER, mColorOnlyFBO);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.8f);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mColorOnlyFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mMRTFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, mMRTFBO);
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::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mMRTColorBuffer0,
0);
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::blue);
EXPECT_PIXEL_COLOR_EQ(3 * getWindowWidth() / 4, 3 * getWindowHeight() / 4, GLColor::yellow);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mMRTColorBuffer0,
0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
mMRTColorBuffer1, 0);
}
// Test multisampled framebuffer blits if supported
TEST_P(BlitFramebufferANGLETest, MultisampledRGBAToRGBA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
if (!checkExtension("GL_ANGLE_framebuffer_multisample"))
return;
if (!checkExtension("GL_OES_rgb8_rgba8"))
return;
multisampleTestHelper(mRGBAMultisampledFBO, mRGBAFBO);
}
TEST_P(BlitFramebufferANGLETest, MultisampledRGBAToBGRA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
if (!checkExtension("GL_ANGLE_framebuffer_multisample"))
return;
if (!checkExtension("GL_OES_rgb8_rgba8"))
return;
if (!checkExtension("GL_EXT_texture_format_BGRA8888"))
return;
multisampleTestHelper(mRGBAMultisampledFBO, mBGRAFBO);
}
TEST_P(BlitFramebufferANGLETest, MultisampledBGRAToRGBA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
if (!checkExtension("GL_ANGLE_framebuffer_multisample"))
return;
if (!checkExtension("GL_OES_rgb8_rgba8"))
return;
if (!checkExtension("GL_EXT_texture_format_BGRA8888"))
return;
multisampleTestHelper(mBGRAMultisampledFBO, mRGBAFBO);
}
TEST_P(BlitFramebufferANGLETest, MultisampledBGRAToBGRA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
if (!checkExtension("GL_ANGLE_framebuffer_multisample"))
return;
if (!checkExtension("GL_OES_rgb8_rgba8"))
return;
if (!checkExtension("GL_EXT_texture_format_BGRA8888"))
return;
multisampleTestHelper(mBGRAMultisampledFBO, mBGRAFBO);
}
// Make sure that attempts to stretch in a blit call issue an error
TEST_P(BlitFramebufferANGLETest, ErrorStretching)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth() / 2, getWindowHeight() / 2, 0, 0,
getWindowWidth(), getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Make sure that attempts to flip in a blit call issue an error
TEST_P(BlitFramebufferANGLETest, ErrorFlipping)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth() / 2, getWindowHeight() / 2, getWindowWidth() / 2,
getWindowHeight() / 2, 0, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
TEST_P(BlitFramebufferANGLETest, Errors)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit"));
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mCheckerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mOriginalFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, mUserFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT | 234, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, mDiffFormatFBO);
glBlitFramebufferANGLE(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// TODO(geofflang): Fix the dependence on glBlitFramebufferANGLE without checks and assuming the
// default framebuffer is BGRA to enable the GL and GLES backends. (http://anglebug.com/42260299)
class BlitFramebufferTest : public ANGLETest<>
{
protected:
BlitFramebufferTest()
{
setWindowWidth(256);
setWindowHeight(256);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
setConfigStencilBits(8);
}
void initColorFBO(GLFramebuffer *fbo,
GLRenderbuffer *rbo,
GLenum rboFormat,
GLsizei width,
GLsizei height)
{
glBindRenderbuffer(GL_RENDERBUFFER, *rbo);
glRenderbufferStorage(GL_RENDERBUFFER, rboFormat, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *rbo);
}
void initColorFBOWithCheckerPattern(GLFramebuffer *fbo,
GLRenderbuffer *rbo,
GLenum rboFormat,
GLsizei width,
GLsizei height)
{
initColorFBO(fbo, rbo, rboFormat, width, height);
ANGLE_GL_PROGRAM(checkerProgram, essl1_shaders::vs::Passthrough(),
essl1_shaders::fs::Checkered());
glViewport(0, 0, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
drawQuad(checkerProgram, 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);
}
// Test blitting between 3D textures and 2D array textures
void test3DBlit(GLenum sourceTarget, GLenum destTarget)
{
constexpr int kTexWidth = 4;
constexpr int kTexHeight = 3;
constexpr int kTexDepth = 2;
glViewport(0, 0, kTexWidth, kTexHeight);
size_t size = kTexWidth * kTexHeight * kTexDepth;
std::vector<uint32_t> sourceData(size);
std::vector<uint32_t> destData(size);
for (size_t i = 0; i < size; ++i)
{
sourceData[i] = i;
destData[i] = size - i;
}
// Create a source 3D texture and FBO.
GLTexture sourceTexture;
glBindTexture(sourceTarget, sourceTexture);
glTexImage3D(sourceTarget, 0, GL_RGBA8, kTexWidth, kTexHeight, kTexDepth, 0, GL_RGBA,
GL_UNSIGNED_BYTE, sourceData.data());
// Create a dest texture and FBO.
GLTexture destTexture;
glBindTexture(destTarget, destTexture);
glTexImage3D(destTarget, 0, GL_RGBA8, kTexWidth, kTexHeight, kTexDepth, 0, GL_RGBA,
GL_UNSIGNED_BYTE, destData.data());
for (int z = 0; z < kTexDepth; ++z)
{
ASSERT_GL_NO_ERROR();
GLFramebuffer sourceFBO;
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, sourceTexture, 0,
z);
ASSERT_GL_NO_ERROR();
GLFramebuffer destFBO;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destFBO);
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, destTexture, 0, z);
ASSERT_GL_NO_ERROR();
glBlitFramebuffer(0, 0, kTexWidth, kTexHeight, 0, 0, kTexWidth, kTexHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
}
for (int z = 0; z < kTexDepth; ++z)
{
GLFramebuffer readFBO;
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFBO);
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, destTexture, 0, z);
ASSERT_GL_NO_ERROR();
glReadBuffer(GL_COLOR_ATTACHMENT0);
for (int y = 0; y < kTexHeight; ++y)
{
for (int x = 0; x < kTexWidth; ++x)
{
int index = x + kTexWidth * (y + z * kTexHeight);
EXPECT_PIXEL_COLOR_EQ(x, y, index);
}
}
}
}
void initFBOWithProgramAndDepth(GLFramebuffer *fbo,
GLRenderbuffer *colorRenderBuffer,
GLenum colorFormat,
GLRenderbuffer *depthRenderBuffer,
GLenum depthFormat,
GLsizei width,
GLsizei height,
GLuint program,
float depthValue)
{
if (fbo != nullptr)
{
// Create renderbuffer
glBindRenderbuffer(GL_RENDERBUFFER, *colorRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, colorFormat, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, *depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, width, height);
// Create fbo
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
*colorRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
*depthRenderBuffer);
}
else
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// draw with program
glUseProgram(program);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepthf(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
drawQuad(program, essl1_shaders::PositionAttrib(), depthValue);
}
void drawWithDepthValue(std::array<Vector3, 6> &quadVertices, float depth)
{
for (Vector3 &vertice : quadVertices)
{
vertice[2] = depth;
}
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quadVertices[0]) * quadVertices.size(),
quadVertices.data());
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void BlitSampleStencilToDefault(GLsizei samples)
{
GLsizei w = 3, h = 2;
GLint stencilValue = 1;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLRenderbuffer colorbuf;
glBindRenderbuffer(GL_RENDERBUFFER, colorbuf);
if (samples != 0)
{
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, w, h);
}
else
{
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h);
}
GLRenderbuffer depthstencilbuf;
glBindRenderbuffer(GL_RENDERBUFFER, depthstencilbuf);
if (samples != 0)
{
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, w, h);
}
else
{
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
}
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
depthstencilbuf);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthstencilbuf);
glCheckFramebufferStatus(GL_FRAMEBUFFER);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glFlush();
// Clear the stencil of each pixel to a different value.
glEnable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
glViewport(0, 0, w, h);
for (GLsizei y = 0; y < h; y++)
{
for (GLsizei x = 0; x < w; x++)
{
glScissor(x, y, 1, 1);
glClearStencil(stencilValue);
glClear(GL_STENCIL_BUFFER_BIT);
stencilValue += 1;
}
}
glDisable(GL_SCISSOR_TEST);
// Blit stencil buffer to default framebuffer.
GLenum attachments1[] = {GL_COLOR_ATTACHMENT0};
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
// Disable stencil and draw full_screen green color.
ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
glDisable(GL_STENCIL_TEST);
drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.5f);
// Get the stencil through drawQuad.
// If the blit finished successfully, the stencil test should all pass.
ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUseProgram(drawColor);
glEnable(GL_STENCIL_TEST);
for (GLsizei i = 1; i <= 6; i++)
{
glStencilFunc(GL_EQUAL, i, 255);
glUniform4f(colorUniformLocation, i * 1.0f / 255, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 1.0f);
ASSERT_GL_NO_ERROR();
}
// Check the result.
std::vector<GLColor> pixels(w * h);
GLuint color_ref = 1;
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
for (GLsizei y = 0; y < h; y++)
{
for (GLsizei x = 0; x < w; x++)
{
const int curPos = y * w + x;
EXPECT_COLOR_NEAR(GLColor(color_ref, 0, 0, 255), pixels[curPos], 1);
color_ref += 1;
}
}
}
};
class BlitFramebufferTestES31 : public BlitFramebufferTest
{};
// Tests resolving a multisample depth buffer.
TEST_P(BlitFramebufferTest, MultisampleDepth)
{
// TODO([email protected]): http://crbug.com/837717
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsMac());
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_DEPTH_COMPONENT24, 256, 256);
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuf);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClearDepthf(0.5f);
glClear(GL_DEPTH_BUFFER_BIT);
GLRenderbuffer destRenderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, destRenderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 256, 256);
GLFramebuffer resolved;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolved);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
destRenderbuf);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, resolved);
// Immediately destroy the framebuffer and the associated textures for additional cleanup
// ordering testing.
framebuffer.reset();
renderbuf.reset();
GLTexture colorbuf;
glBindTexture(GL_TEXTURE_2D, colorbuf);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuf, 0);
ASSERT_GL_NO_ERROR();
// Clear to green
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Make sure resulting depth is near 0.5f.
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthMask(false);
glDepthFunc(GL_LESS);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), -0.01f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(255, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, 255, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(255, 255, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(127, 127, 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, essl3_shaders::PositionAttrib(), 0.01f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(255, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(0, 255, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(255, 255, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(127, 127, GLColor::blue);
ASSERT_GL_NO_ERROR();
}
// Blit stencil buffer to default framebuffer with prerotaion.
TEST_P(BlitFramebufferTest, BlitStencilToDefault)
{
BlitSampleStencilToDefault(0);
}
// Blit multisample stencil buffer to default framebuffer with prerotaion.
TEST_P(BlitFramebufferTest, BlitMultisampleStencilToDefault)
{
BlitSampleStencilToDefault(4);
}
// Test blit multisampled framebuffer to MRT framebuffer
TEST_P(BlitFramebufferTest, BlitMultisampledFramebufferToMRT)
{
// https://issues.angleproject.org/issues/361369302
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Prepare multisampled framebuffer to blit from.
GLRenderbuffer multiSampleColorbuf;
glBindRenderbuffer(GL_RENDERBUFFER, multiSampleColorbuf);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, getWindowWidth(),
getWindowHeight());
GLFramebuffer multiSampleFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, multiSampleFramebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
multiSampleColorbuf);
glCheckFramebufferStatus(GL_FRAMEBUFFER);
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.8f);
EXPECT_GL_NO_ERROR();
// Prepare mrt framebuffer with two attachments to blit to.
GLFramebuffer MRTFBO;
glBindFramebuffer(GL_FRAMEBUFFER, MRTFBO);
GLTexture MRTColorBuffer0;
GLTexture MRTColorBuffer1;
glBindTexture(GL_TEXTURE_2D, MRTColorBuffer0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, MRTColorBuffer0, 0);
glBindTexture(GL_TEXTURE_2D, MRTColorBuffer1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, MRTColorBuffer1, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, multiSampleFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, MRTFBO);
glReadBuffer(GL_COLOR_ATTACHMENT0);
GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, drawBuffers);
glBlitFramebuffer(0, 0, getWindowWidth(), getWindowHeight(), 0, 0, getWindowWidth(),
getWindowHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
// Check results
glBindFramebuffer(GL_FRAMEBUFFER, MRTFBO);
glReadBuffer(GL_COLOR_ATTACHMENT0);
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red);
glReadBuffer(GL_COLOR_ATTACHMENT1);
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red);
}
// Tests clearing a multisampled depth buffer.
TEST_P(BlitFramebufferTest, MultisampleDepthClear)
{
// http://anglebug.com/40096654
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
GLRenderbuffer depthMS;
glBindRenderbuffer(GL_RENDERBUFFER, depthMS);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_DEPTH_COMPONENT24, 256, 256);
GLRenderbuffer colorMS;
glBindRenderbuffer(GL_RENDERBUFFER, colorMS);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_RGBA8, 256, 256);
GLRenderbuffer colorResolved;
glBindRenderbuffer(GL_RENDERBUFFER, colorResolved);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 256, 256);
GLFramebuffer framebufferMS;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorMS);
// Clear depth buffer to 0.5 and color to green.
glClearDepthf(0.5f);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glFlush();
// Draw red into the multisampled color buffer.
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_EQUAL);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.0f);
// Resolve the color buffer to make sure the above draw worked correctly, which in turn implies
// that the multisampled depth clear worked.
GLFramebuffer framebufferResolved;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferResolved);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorResolved);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferMS);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferResolved);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(255, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, 255, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(255, 255, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(127, 127, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Tests clearing a multisampled depth buffer with a glFenceSync in between.
TEST_P(BlitFramebufferTest, MultisampleDepthClearWithFenceSync)
{
// http://anglebug.com/40096654
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
GLRenderbuffer depthMS;
glBindRenderbuffer(GL_RENDERBUFFER, depthMS);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_DEPTH_COMPONENT24, 256, 256);
GLRenderbuffer colorMS;
glBindRenderbuffer(GL_RENDERBUFFER, colorMS);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_RGBA8, 256, 256);
GLRenderbuffer colorResolved;
glBindRenderbuffer(GL_RENDERBUFFER, colorResolved);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 256, 256);
GLFramebuffer framebufferMS;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorMS);
// Clear depth buffer to 0.5 and color to green.
glClearDepthf(0.5f);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glFlush();
// Draw red into the multisampled color buffer.
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_EQUAL);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.0f);
// This should trigger a deferred renderPass end
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
EXPECT_GL_NO_ERROR();
// Resolve the color buffer to make sure the above draw worked correctly, which in turn implies
// that the multisampled depth clear worked.
GLFramebuffer framebufferResolved;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferResolved);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorResolved);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferMS);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferResolved);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(255, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, 255, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(255, 255, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(127, 127, GLColor::red);
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
ASSERT_GL_NO_ERROR();
}
// Test resolving a multisampled stencil buffer.
TEST_P(BlitFramebufferTest, MultisampleStencil)
{
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_STENCIL_INDEX8, 256, 256);
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuf);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// fill the stencil buffer with 0x1
glStencilFunc(GL_ALWAYS, 0x1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glEnable(GL_STENCIL_TEST);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
GLTexture destColorbuf;
glBindTexture(GL_TEXTURE_2D, destColorbuf);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256);
GLRenderbuffer destRenderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, destRenderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, 256, 256);
GLFramebuffer resolved;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolved);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destColorbuf,
0);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
destRenderbuf);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_STENCIL_BUFFER_BIT, GL_NEAREST);
// Immediately destroy the framebuffer and the associated textures for additional cleanup
// ordering testing.
framebuffer.reset();
renderbuf.reset();
glBindFramebuffer(GL_FRAMEBUFFER, resolved);
ASSERT_GL_NO_ERROR();
// Clear to green
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Draw red if the stencil is 0x1, which should be true after the resolve.
glStencilFunc(GL_EQUAL, 0x1, 0xFF);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test resolving a multisampled stencil buffer with scissor.
TEST_P(BlitFramebufferTest, ScissoredMultisampleStencil)
{
constexpr GLuint kSize = 256;
// Create the resolve framebuffer.
GLTexture destColorbuf;
glBindTexture(GL_TEXTURE_2D, destColorbuf);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
GLRenderbuffer destRenderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, destRenderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, kSize, kSize);
GLFramebuffer resolved;
glBindFramebuffer(GL_FRAMEBUFFER, resolved);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destColorbuf,
0);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
destRenderbuf);
// Clear the resolved buffer with gray and 0x10 stencil.
GLColor gray(127, 127, 127, 255);
glClearStencil(0x10);
glClearColor(0.499f, 0.499f, 0.499f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, gray);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, gray);
// Create the multisampled framebuffer.
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_STENCIL_INDEX8, kSize, kSize);
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
ANGLE_GL_PROGRAM(drawBlue, essl3_shaders::vs::Simple(), essl3_shaders::fs::Blue());
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuf);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Fill the stencil buffer with 0x1.
glClearStencil(0x1);
glClear(GL_STENCIL_BUFFER_BIT);
// Fill a smaller region of the buffer with 0x2.
glEnable(GL_SCISSOR_TEST);
glEnable(GL_STENCIL_TEST);
glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
glStencilFunc(GL_ALWAYS, 0x2, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
// Blit into the resolved framebuffer (with scissor still enabled).
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolved);
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_STENCIL_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, resolved);
ASSERT_GL_NO_ERROR();
// Draw blue if the stencil is 0x1, which should never be true.
glDisable(GL_SCISSOR_TEST);
glStencilMask(0);
glStencilFunc(GL_EQUAL, 0x1, 0xFF);
drawQuad(drawBlue, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, gray);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, gray);
// Draw red if the stencil is 0x2, which should be true in the middle after the blit/resolve.
glStencilFunc(GL_EQUAL, 0x2, 0xFF);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, gray);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
// Draw green if the stencil is 0x10, which should be left untouched in the outer regions.
glStencilFunc(GL_EQUAL, 0x10, 0xFF);
drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test blitting from a texture with non-zero base. The blit is non-stretching and between
// identical formats so that the path that uses vkCmdBlitImage is taken.
TEST_P(BlitFramebufferTest, NonZeroBaseSource)
{
// http://anglebug.com/40644751
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsMac());
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
// Create a framebuffer for source data. It usea a non-zero base.
GLTexture srcColor;
glBindTexture(GL_TEXTURE_2D, srcColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcColor, 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// fill the color buffer with red.
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
// Create a framebuffer for blit destination.
GLTexture dstColor;
glBindTexture(GL_TEXTURE_2D, dstColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer dstFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstColor, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Blit. Note: no stretching is done so that vkCmdBlitImage can be used.
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
ASSERT_GL_NO_ERROR();
// Make sure the blit is done.
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test blitting to a texture with non-zero base. The blit is non-stretching and between
// identical formats so that the path that uses vkCmdBlitImage is taken.
TEST_P(BlitFramebufferTest, NonZeroBaseDestination)
{
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
// Create a framebuffer for source data. It usea a non-zero base.
GLTexture srcColor;
glBindTexture(GL_TEXTURE_2D, srcColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcColor, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// fill the color buffer with red.
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
// Create a framebuffer for blit destination.
GLTexture dstColor;
glBindTexture(GL_TEXTURE_2D, dstColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
GLFramebuffer dstFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstColor, 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Blit. Note: no stretching is done so that vkCmdBlitImage can be used.
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
ASSERT_GL_NO_ERROR();
// Make sure the blit is done.
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test blitting from a stencil buffer with non-zero base.
TEST_P(BlitFramebufferTest, NonZeroBaseSourceStencil)
{
// http://anglebug.com/40644751
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsMac());
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
// Create a framebuffer with an attachment that has non-zero base
GLTexture stencilTexture;
glBindTexture(GL_TEXTURE_2D, stencilTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 256, 256, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, nullptr);
glTexImage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, 256, 256, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, stencilTexture, 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// fill the stencil buffer with 0x1
glStencilFunc(GL_ALWAYS, 0x1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glEnable(GL_STENCIL_TEST);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
// Create a framebuffer with an attachment that has non-zero base
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256);
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 256, 256);
GLFramebuffer dstFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuf);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Blit stencil.
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_STENCIL_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
ASSERT_GL_NO_ERROR();
// Clear to green
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Draw red if the stencil is 0x1, which should be true after the blit.
glStencilFunc(GL_EQUAL, 0x1, 0xFF);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test blitting to a stencil buffer with non-zero base.
TEST_P(BlitFramebufferTest, NonZeroBaseDestinationStencil)
{
// http://anglebug.com/40644751
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsMac());
// http://anglebug.com/42263576
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
// Create a framebuffer for source data.
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 256, 256);
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuf);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// fill the stencil buffer with 0x1
glStencilFunc(GL_ALWAYS, 0x1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glEnable(GL_STENCIL_TEST);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
// Create a framebuffer with an attachment that has non-zero base
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256);
GLTexture stencilTexture;
glBindTexture(GL_TEXTURE_2D, stencilTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 256, 256, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, nullptr);
glTexImage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, 256, 256, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
GLFramebuffer dstFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, stencilTexture, 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Blit stencil.
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_STENCIL_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
ASSERT_GL_NO_ERROR();
// Clear to green
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Draw red if the stencil is 0x1, which should be true after the blit.
glStencilFunc(GL_EQUAL, 0x1, 0xFF);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test blitting to a stencil buffer with non-zero base. Exercises the compute path in the Vulkan
// backend if stencil export is not supported. The blit is not 1-to-1 for this path to be taken.
TEST_P(BlitFramebufferTest, NonZeroBaseDestinationStencilStretch)
{
// http://anglebug.com/40644750
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
// http://anglebug.com/40644751
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsMac());
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
// Create a framebuffer for source data.
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 256, 256);
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuf);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// fill the stencil buffer with 0x1
glStencilFunc(GL_ALWAYS, 0x1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glEnable(GL_STENCIL_TEST);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
// Create a framebuffer with an attachment that has non-zero base
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 256, 256);
GLTexture stencilTexture;
glBindTexture(GL_TEXTURE_2D, stencilTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 256, 256, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, nullptr);
glTexImage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, 256, 256, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
GLFramebuffer dstFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, stencilTexture, 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Blit stencil. Note: stretch is intentional so vkCmdBlitImage cannot be used in the Vulkan
// backend.
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBlitFramebuffer(0, 0, 256, 256, -256, -256, 512, 512, GL_STENCIL_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
ASSERT_GL_NO_ERROR();
// Clear to green
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Draw red if the stencil is 0x1, which should be true after the blit.
glStencilFunc(GL_EQUAL, 0x1, 0xFF);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Blit an SRGB framebuffer and scale it.
TEST_P(BlitFramebufferTest, BlitSRGBToRGBAndScale)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer sourceRBO, targetRBO;
GLFramebuffer sourceFBO, targetFBO;
initColorFBOWithCheckerPattern(&sourceFBO, &sourceRBO, GL_SRGB8_ALPHA8, kWidth * 2,
kHeight * 2);
initColorFBO(&targetFBO, &targetRBO, GL_RGBA8, kWidth, kHeight);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glViewport(0, 0, kWidth, kHeight);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Scale down without flipping.
glBlitFramebuffer(0, 0, kWidth * 2, kHeight * 2, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::yellow);
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Scale down and flip in the X direction.
glBlitFramebuffer(0, 0, kWidth * 2, kHeight * 2, kWidth, 0, 0, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::green);
}
// Blit stencil, with scissor and scale it.
TEST_P(BlitFramebufferTest, BlitStencilScissoredScaled)
{
constexpr GLint kSize = 256;
// Create the destination framebuffer.
GLTexture destColorbuf;
glBindTexture(GL_TEXTURE_2D, destColorbuf);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
GLRenderbuffer destRenderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, destRenderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, kSize, kSize);
GLFramebuffer destFBO;
glBindFramebuffer(GL_FRAMEBUFFER, destFBO);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destColorbuf,
0);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
destRenderbuf);
// Clear the destination buffer with gray and 0x10 stencil.
GLColor gray(127, 127, 127, 255);
glClearStencil(0x10);
glClearColor(0.499f, 0.499f, 0.499f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, gray);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, gray);
// Create the source framebuffer.
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, kSize, kSize);
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
ANGLE_GL_PROGRAM(drawBlue, essl3_shaders::vs::Simple(), essl3_shaders::fs::Blue());
GLFramebuffer sourceFBO;
glBindFramebuffer(GL_FRAMEBUFFER, sourceFBO);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuf);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Fill the stencil buffer with 0x1.
glClearStencil(0x1);
glClear(GL_STENCIL_BUFFER_BIT);
// Fill a smaller region of the buffer with 0x2.
glEnable(GL_SCISSOR_TEST);
glEnable(GL_STENCIL_TEST);
glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
glStencilFunc(GL_ALWAYS, 0x2, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
// Blit and scale down into the destination framebuffer (with scissor still enabled).
//
// Source looks like this:
//
// +----|----|----|----+
// | |
// | 0x1 |
// - +---------+ -
// | | | |
// | | | |
// - | 0x2 | -
// | | | |
// | | | |
// - +---------+ -
// | |
// | |
// +----|----|----|----+
//
// We want the destination to look like this:
//
// +----|----|----|----+
// | |
// | 0x10 |
// - +---------+ -
// | | 0x1 | |
// | | +------+ |
// - | | | -
// | | | 0x2 | |
// | | | | |
// - +--+------+ -
// | |
// | |
// +----|----|----|----+
//
// The corresponding blit would be: (0, 0, 3/4, 3/4) -> (1/4, 1/4, 3/4, 3/4). For testing, we
// would like to avoid having the destination area and scissor to match. Using destination
// area as (0, 0, 1, 1), and keeping the same scaling, the source area should be
// (-3/8, -3/8, 9/8, 9/8).
//
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destFBO);
constexpr GLint kBlitSrc[2] = {-3 * kSize / 8, 9 * kSize / 8};
glBlitFramebuffer(kBlitSrc[0], kBlitSrc[0], kBlitSrc[1], kBlitSrc[1], 0, 0, kSize, kSize,
GL_STENCIL_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, destFBO);
ASSERT_GL_NO_ERROR();
// Draw blue if the stencil is 0x1, which should be true only in the top and left of the inner
// square.
glDisable(GL_SCISSOR_TEST);
glStencilMask(0);
glStencilFunc(GL_EQUAL, 0x1, 0xFF);
drawQuad(drawBlue, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, gray);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize / 4, kSize / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 4, 3 * kSize / 4 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, kSize / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, gray);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, 3 * kSize / 4 - 1, gray);
// Draw red if the stencil is 0x2, which should be true in the bottom/right of the middle
// square after the blit.
glStencilFunc(GL_EQUAL, 0x2, 0xFF);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, gray);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, gray);
EXPECT_PIXEL_COLOR_EQ(kSize / 4, kSize / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 4, 3 * kSize / 4 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, kSize / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, 3 * kSize / 4 - 1, GLColor::red);
// Draw green if the stencil is 0x10, which should be left untouched in the outer regions.
glStencilFunc(GL_EQUAL, 0x10, 0xFF);
drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kSize / 4, kSize / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 4, 3 * kSize / 4 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, kSize / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, 3 * kSize / 4 - 1, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Blit a subregion of an SRGB framebuffer to an RGB framebuffer.
TEST_P(BlitFramebufferTest, PartialBlitSRGBToRGB)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer sourceRBO, targetRBO;
GLFramebuffer sourceFBO, targetFBO;
initColorFBOWithCheckerPattern(&sourceFBO, &sourceRBO, GL_SRGB8_ALPHA8, kWidth * 2,
kHeight * 2);
initColorFBO(&targetFBO, &targetRBO, GL_RGBA8, kWidth, kHeight);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glViewport(0, 0, kWidth, kHeight);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Blit a part of the source FBO without flipping.
glBlitFramebuffer(kWidth, kHeight, kWidth * 2, kHeight * 2, 0, 0, kWidth, kHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::yellow);
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Blit a part of the source FBO and flip in the X direction.
glBlitFramebuffer(kWidth * 2, 0, kWidth, kHeight, kWidth, 0, 0, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::blue);
}
// Blit an SRGB framebuffer with an oversized source area (parts outside the source area should be
// clipped out).
TEST_P(BlitFramebufferTest, BlitSRGBToRGBOversizedSourceArea)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer sourceRBO, targetRBO;
GLFramebuffer sourceFBO, targetFBO;
initColorFBOWithCheckerPattern(&sourceFBO, &sourceRBO, GL_SRGB8_ALPHA8, kWidth, kHeight);
initColorFBO(&targetFBO, &targetRBO, GL_RGBA8, kWidth, kHeight);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Blit so that the source area gets placed at the center of the target FBO.
// The width of the source area is 1/4 of the width of the target FBO.
glBlitFramebuffer(-3 * kWidth / 2, -3 * kHeight / 2, 5 * kWidth / 2, 5 * kHeight / 2, 0, 0,
kWidth, kHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
// Source FBO colors can be found in the middle of the target FBO.
EXPECT_PIXEL_COLOR_EQ(7 * kWidth / 16, 7 * kHeight / 16, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(7 * kWidth / 16, 9 * kHeight / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(9 * kWidth / 16, 7 * kHeight / 16, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(9 * kWidth / 16, 9 * kHeight / 16, GLColor::yellow);
// Clear color should remain around the edges of the target FBO (WebGL 2.0 spec explicitly
// requires this and ANGLE is expected to follow that).
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::blue);
}
// Blit an SRGB framebuffer with an oversized dest area (even though the result is clipped, it
// should be scaled as if the whole dest area was used).
TEST_P(BlitFramebufferTest, BlitSRGBToRGBOversizedDestArea)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer sourceRBO, targetRBO;
GLFramebuffer sourceFBO, targetFBO;
initColorFBOWithCheckerPattern(&sourceFBO, &sourceRBO, GL_SRGB8_ALPHA8, kWidth, kHeight);
initColorFBO(&targetFBO, &targetRBO, GL_RGBA8, kWidth, kHeight);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Dest is oversized but centered the same as source
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glBlitFramebuffer(0, 0, kWidth, kHeight, -kWidth / 2, -kHeight / 2, 3 * kWidth / 2,
3 * kHeight / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
// Expected result:
//
// +-------+-------+
// | | |
// | R | B |
// | | |
// +-------+-------+
// | | |
// | G | Y |
// | | |
// +-------+-------+
//
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(1, kWidth - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 + 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 + 1, GLColor::yellow);
// Dest is oversized in the negative direction
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glBlitFramebuffer(0, 0, kWidth, kHeight, -kWidth / 2, -kHeight / 2, kWidth, kHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
// Expected result:
//
// Width / 4
// |
// V
// +---+-----------+
// | R | B |
// +---+-----------+ <- Height / 4
// | | |
// | | |
// | G | Y |
// | | |
// | | |
// +---+-----------+
//
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, kHeight / 4 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 - 1, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 - 1, kHeight / 4 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 8, kHeight / 8, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, kHeight / 4 + 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 - 1, kHeight / 4 + 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 - 1, kHeight - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 8, kHeight / 2, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 + 1, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 + 1, kHeight / 4 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight / 4 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 + 1, kHeight / 4 + 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4 + 1, kHeight - 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight / 4 + 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::yellow);
// Dest is oversized in the positive direction
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, 3 * kWidth / 2, 3 * kHeight / 2,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
// Expected result:
//
// 3 * Width / 4
// |
// V
// +-----------+---+
// | | |
// | | |
// | R | B |
// | | |
// | | |
// +-----------+---+ <- 3 * Height / 4
// | G | Y |
// +-----------+---+
//
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, 3 * kHeight / 4 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 - 1, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 - 1, 3 * kHeight / 4 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2, kHeight / 2, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, 3 * kHeight / 4 + 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, kHeight - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 - 1, 3 * kHeight / 4 + 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 - 1, kHeight - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2, 7 * kHeight / 8, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 + 1, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 + 1, 3 * kHeight / 4 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 3 * kHeight / 4 - 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(7 * kWidth / 8, kHeight / 2, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 + 1, 3 * kHeight / 4 + 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4 + 1, kHeight - 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 3 * kHeight / 4 + 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(7 * kWidth / 8, 7 * kHeight / 8, GLColor::yellow);
}
// This test is to demonstrate a bug that when a program is created and used and then destroyed, we
// should not have a dangling PipelineHelper pointer in the context point to the already destroyed
// object.
TEST_P(BlitFramebufferTest, useAndDestroyProgramThenBlit)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer sourceRBO, targetRBO;
GLFramebuffer sourceFBO, targetFBO;
{
initColorFBO(&sourceFBO, &sourceRBO, GL_SRGB8_ALPHA8, kWidth, kHeight);
// checkerProgram will be created and destroyed in this code block
ANGLE_GL_PROGRAM(checkerProgram, essl1_shaders::vs::Passthrough(),
essl1_shaders::fs::Checkered());
glViewport(0, 0, kWidth, kHeight);
glBindFramebuffer(GL_FRAMEBUFFER, sourceFBO);
drawQuad(checkerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
}
initColorFBO(&targetFBO, &targetRBO, GL_RGBA8, kWidth, kHeight);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Blit call should not crash or assert
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glBlitFramebuffer(0, 0, kWidth, kHeight, -kWidth / 2, -kHeight / 2, 3 * kWidth / 2,
3 * kHeight / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
}
// This test is to ensure the draw after blit without any state change works properly
TEST_P(BlitFramebufferTest, drawBlitAndDrawAgain)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer srcColorRB, srcDepthRB;
GLFramebuffer srcFBO;
ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Red());
ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Green());
ANGLE_GL_PROGRAM(drawBlue, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Blue());
// Initialize source FBO with red color and depth==0.8f
initFBOWithProgramAndDepth(&srcFBO, &srcColorRB, GL_RGBA8, &srcDepthRB, GL_DEPTH24_STENCIL8_OES,
kWidth, kHeight, drawRed, 0.8f);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
// Initialize destination FBO and initialize to green and depth==0.7
initFBOWithProgramAndDepth(nullptr, nullptr, 0, nullptr, 0, kWidth, kHeight, drawGreen, 0.7f);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
// Setup for draw-blit-draw use pattern
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
std::array<Vector3, 6> quadVertices = GetQuadVertices();
constexpr size_t kBufferSize = sizeof(quadVertices[0]) * quadVertices.size();
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, kBufferSize, nullptr, GL_STATIC_DRAW);
glUseProgram(drawBlue);
const GLint positionLocation = glGetAttribLocation(drawBlue, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLocation);
// Draw with depth=0.75, should fail depth test
drawWithDepthValue(quadVertices, 0.75f);
// Now blit depth buffer from source FBO to the right half of destination FBO, so left half has
// depth 0.7f and right half has 0.8f
glBlitFramebuffer(kWidth / 2, 0, kWidth, kHeight, kWidth / 2, 0, kWidth, kHeight,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
// Continue draw without state change and depth==0.75f, now it should pass depth test on right
// half
glDrawArrays(GL_TRIANGLES, 0, 6);
// Now verify dstFBO
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, 1, GLColor::blue);
EXPECT_GL_NO_ERROR();
}
// This test is to ensure the scissored draw after blit without any state change works properly
TEST_P(BlitFramebufferTest, scissorDrawBlitAndDrawAgain)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer srcColorRB, srcDepthRB;
GLFramebuffer srcFBO;
ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Red());
ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Green());
ANGLE_GL_PROGRAM(drawBlue, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Blue());
// Initialize source FBO with red color and depth==0.8f
initFBOWithProgramAndDepth(&srcFBO, &srcColorRB, GL_RGBA8, &srcDepthRB, GL_DEPTH24_STENCIL8_OES,
kWidth, kHeight, drawRed, 0.8f);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
// Initialize destination FBO and initialize to green and depth==0.7
initFBOWithProgramAndDepth(nullptr, nullptr, 0, nullptr, 0, kWidth, kHeight, drawGreen, 0.7f);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
// Setup for draw-blit-draw use pattern
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
std::array<Vector3, 6> quadVertices = GetQuadVertices();
constexpr size_t kBufferSize = sizeof(quadVertices[0]) * quadVertices.size();
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, kBufferSize, nullptr, GL_STATIC_DRAW);
glUseProgram(drawBlue);
const GLint positionLocation = glGetAttribLocation(drawBlue, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLocation);
// Scissored draw with depth=0.75, should fail depth test
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
drawWithDepthValue(quadVertices, 0.75f);
// Now blit depth buffer from source FBO to the right half of destination FBO, so left half has
// depth 0.7f and right half has 0.8f
glBlitFramebuffer(kWidth / 2, 0, kWidth, kHeight, kWidth / 2, 0, kWidth, kHeight,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
// Continue draw without state change and depth==0.75f, now it should pass depth test on right
// half
glDrawArrays(GL_TRIANGLES, 0, 6);
// Now verify dstFBO
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, 1, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(1, kHeight - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight - 1, GLColor::green);
EXPECT_GL_NO_ERROR();
}
// Test blitFramebuffer size overflow checks. WebGL 2.0 spec section 5.41. We do validation for
// overflows also in non-WebGL mode to avoid triggering driver bugs.
TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow)
{
GLTexture textures[2];
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 4, 4);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 4, 4);
GLFramebuffer framebuffers[2];
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[1]);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0],
0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1],
0);
ASSERT_GL_NO_ERROR();
// srcX
glBlitFramebuffer(-1, 0, std::numeric_limits<GLint>::max(), 4, 0, 0, 4, 4, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glBlitFramebuffer(std::numeric_limits<GLint>::max(), 0, -1, 4, 0, 0, 4, 4, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// srcY
glBlitFramebuffer(0, -1, 4, std::numeric_limits<GLint>::max(), 0, 0, 4, 4, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glBlitFramebuffer(0, std::numeric_limits<GLint>::max(), 4, -1, 0, 0, 4, 4, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// dstX
glBlitFramebuffer(0, 0, 4, 4, -1, 0, std::numeric_limits<GLint>::max(), 4, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glBlitFramebuffer(0, 0, 4, 4, std::numeric_limits<GLint>::max(), 0, -1, 4, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// dstY
glBlitFramebuffer(0, 0, 4, 4, 0, -1, 4, std::numeric_limits<GLint>::max(), GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glBlitFramebuffer(0, 0, 4, 4, 0, std::numeric_limits<GLint>::max(), 4, -1, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Test blitFramebuffer size overflow checks. WebGL 2.0 spec section 5.41. Similar to above test,
// but this test more accurately duplicates the behavior of the WebGL test
// conformance2/rendering/blitframebuffer-size-overflow.html, which covers a few more edge cases.
TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow2)
{
GLTexture textures[2];
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 4, 4);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 4, 4);
GLFramebuffer framebuffers[2];
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[1]);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0],
0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1],
0);
ASSERT_GL_NO_ERROR();
GLint width = 8;
GLint height = 8;
GLTexture tex0;
glBindTexture(GL_TEXTURE_2D, tex0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
GLFramebuffer fb0;
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0, 0);
GLTexture tex1;
glBindTexture(GL_TEXTURE_2D, tex1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
GLFramebuffer fb1;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb1);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex1, 0);
GLint max = std::numeric_limits<GLint>::max();
// Using max 32-bit integer as blitFramebuffer parameter should succeed.
glBlitFramebuffer(0, 0, max, max, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBlitFramebuffer(0, 0, width, height, 0, 0, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBlitFramebuffer(0, 0, max, max, 0, 0, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
// Using blitFramebuffer parameters where calculated width/height matches max 32-bit integer
// should succeed
glBlitFramebuffer(-1, -1, max - 1, max - 1, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBlitFramebuffer(0, 0, width, height, -1, -1, max - 1, max - 1, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBlitFramebuffer(-1, -1, max - 1, max - 1, -1, -1, max - 1, max - 1, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
// Using source width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(-1, -1, max, max, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using source width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(max, max, -1, -1, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using destination width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(0, 0, width, height, -1, -1, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using destination width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(0, 0, width, height, max, max, -1, -1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using both source and destination width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(-1, -1, max, max, -1, -1, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using minimum and maximum integers for all boundaries should fail.
glBlitFramebuffer(-max - 1, -max - 1, max, max, -max - 1, -max - 1, max, max,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Test an edge case in D3D11 stencil blitting on the CPU that does not properly clip the
// destination regions
TEST_P(BlitFramebufferTest, BlitFramebufferStencilClipNoIntersection)
{
GLFramebuffer framebuffers[2];
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[1]);
GLRenderbuffer renderbuffers[2];
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 4, 4);
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
renderbuffers[0]);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[1]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 4, 4);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
renderbuffers[1]);
glBlitFramebuffer(0, 0, 4, 4, 1 << 24, 1 << 24, 1 << 25, 1 << 25, GL_STENCIL_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
}
// Test that blit with FBOs with multiple color attachments of different size.
TEST_P(BlitFramebufferTest, BlitWithDifferentSizesColorAttachments)
{
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
constexpr GLint kWidth = 32;
constexpr GLint kHeight = 48;
GLColor texture_pattern[kWidth * kHeight];
// Prepare texture pattern
for (int y = 0; y < kHeight; y++)
{
for (int x = 0; x < kWidth; x++)
{
switch ((x + 2 * y) % 3)
{
case 0:
texture_pattern[y * kWidth + x] = GLColor::red;
break;
case 1:
texture_pattern[y * kWidth + x] = GLColor::green;
break;
case 2:
texture_pattern[y * kWidth + x] = GLColor::blue;
break;
default:
break;
}
}
}
GLTexture largeColorBuffer;
glBindTexture(GL_TEXTURE_2D, largeColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
texture_pattern);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, largeColorBuffer,
0);
EXPECT_GL_NO_ERROR();
GLTexture smallColorBuffer;
glBindTexture(GL_TEXTURE_2D, smallColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth / 2, kHeight / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, smallColorBuffer,
0);
EXPECT_GL_NO_ERROR();
GLFramebuffer dstFramebuffer;
GLRenderbuffer dstRenderbuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, dstRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
dstRenderbuffer);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(0, 0, kWidth / 2 + 1, kHeight / 2 + 1, 0, 0, kWidth / 2 + 1, kHeight / 2 + 1,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
for (int x = 0; x < kWidth / 2; x++)
{
for (int y = 0; y < kHeight / 2; y++)
{
switch ((x + 2 * y) % 3)
{
case 0:
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red) << x << " " << y;
break;
case 1:
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green) << x << " " << y;
break;
case 2:
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::blue) << x << " " << y;
break;
default:
break;
}
}
}
EXPECT_GL_NO_ERROR();
}
// Test that blit with FBOs with large color attachment and small depth attachment.
TEST_P(BlitFramebufferTest, BlitLargeColorSmallDepthAttachments)
{
constexpr GLint kWidth = 32;
constexpr GLint kHeight = 48;
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
glDisable(GL_DEPTH_TEST);
GLTexture srcLargeColorBuffer;
glBindTexture(GL_TEXTURE_2D, srcLargeColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcLargeColorBuffer,
0);
EXPECT_GL_NO_ERROR();
GLRenderbuffer srcSmallDepthBuffer;
glBindRenderbuffer(GL_RENDERBUFFER, srcSmallDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth / 2, kHeight / 2);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
srcSmallDepthBuffer);
ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.5f);
GLFramebuffer dstFramebuffer;
GLRenderbuffer dstColorBuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, dstColorBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
dstColorBuffer);
GLRenderbuffer dstDepthBuffer;
glBindRenderbuffer(GL_RENDERBUFFER, dstDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dstDepthBuffer);
glClearDepthf(1.0f);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0);
// The blit is larger than the size of srcFramebuffer, so the actual blit area is (kWidth / 2) *
// (kHeight / 2)
glBlitFramebuffer(0, 0, kWidth / 2 + 1, kHeight / 2 + 1, 0, 0, kWidth / 2 + 1, kHeight / 2 + 1,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
for (int x = 0; x < kWidth / 2; x++)
{
for (int y = 0; y < kHeight / 2; y++)
{
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green) << x << " " << y;
}
}
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthMask(false);
glDepthFunc(GL_LESS);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.7f);
for (int x = 0; x < kWidth / 2; x++)
{
for (int y = 0; y < kHeight / 2; y++)
{
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green) << x << " " << y;
}
}
EXPECT_GL_NO_ERROR();
}
// Test that blit with FBOs with small color attachment and large depth attachment.
TEST_P(BlitFramebufferTest, BlitSmallColorLargeDepthAttachments)
{
constexpr GLint kWidth = 32;
constexpr GLint kHeight = 48;
GLFramebuffer srcFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
glDisable(GL_DEPTH_TEST);
GLTexture srcSmallColorBuffer;
glBindTexture(GL_TEXTURE_2D, srcSmallColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth / 2, kHeight / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcSmallColorBuffer,
0);
EXPECT_GL_NO_ERROR();
GLRenderbuffer srcLargeDepthBuffer;
glBindRenderbuffer(GL_RENDERBUFFER, srcLargeDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
srcLargeDepthBuffer);
ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.5f);
GLFramebuffer dstFramebuffer;
GLRenderbuffer dstColorBuffer;
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, dstColorBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
dstColorBuffer);
GLRenderbuffer dstDepthBuffer;
glBindRenderbuffer(GL_RENDERBUFFER, dstDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dstDepthBuffer);
glClearDepthf(1.0f);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0);
// The blit is larger than the size of srcFramebuffer, so the actual blit area is (kWidth / 2) *
// (kHeight / 2)
glBlitFramebuffer(0, 0, kWidth / 2 + 1, kHeight / 2 + 1, 0, 0, kWidth / 2 + 1, kHeight / 2 + 1,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
for (int x = 0; x < kWidth / 2; x++)
{
for (int y = 0; y < kHeight / 2; y++)
{
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green) << x << " " << y;
}
}
ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
glEnable(GL_DEPTH_TEST);
glDepthMask(false);
glDepthFunc(GL_LESS);
drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.7f);
for (int x = 0; x < kWidth / 2; x++)
{
for (int y = 0; y < kHeight / 2; y++)
{
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green) << x << " " << y;
}
}
EXPECT_GL_NO_ERROR();
}
// Covers an edge case with blitting borderline values.
TEST_P(BlitFramebufferTest, OOBWrite)
{
constexpr size_t length = 0x100000;
GLFramebuffer rfb;
GLFramebuffer dfb;
GLRenderbuffer rb1;
GLRenderbuffer rb2;
glBindFramebuffer(GL_READ_FRAMEBUFFER, rfb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dfb);
glBindRenderbuffer(GL_RENDERBUFFER, rb1);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 0x1000, 2);
glBindRenderbuffer(GL_RENDERBUFFER, rb2);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 2, 2);
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
rb1);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
rb2);
glBlitFramebuffer(1, 0, 0, 1, 1, 0, (2147483648 / 2) - (length / 4) + 1, 1,
GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
}
// Test that flipped blits don't have off-by-one errors
TEST_P(BlitFramebufferTest, FlippedBlits)
{
constexpr const GLsizei kWidth = 11;
constexpr const GLsizei kHeight = 19;
glViewport(0, 0, kWidth, kHeight);
GLRenderbuffer srcColorRB, srcDepthRB, dstColorRB, dstDepthRB;
GLFramebuffer srcFBO, dstFBO;
ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Red());
ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Green());
ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(drawColor);
GLint colorLoc = glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorLoc, -1);
// Create source and dest FBOs
glBindRenderbuffer(GL_RENDERBUFFER, srcColorRB);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glBindRenderbuffer(GL_RENDERBUFFER, srcDepthRB);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, kWidth, kHeight);
glBindFramebuffer(GL_FRAMEBUFFER, srcFBO);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, srcColorRB);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
srcDepthRB);
glBindRenderbuffer(GL_RENDERBUFFER, dstColorRB);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glBindRenderbuffer(GL_RENDERBUFFER, dstDepthRB);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, kWidth, kHeight);
glBindFramebuffer(GL_FRAMEBUFFER, dstFBO);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, dstColorRB);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
dstDepthRB);
// Fill the source framebuffer with differring values per pixel, so off-by-one errors are more
// easily found.
glEnable(GL_SCISSOR_TEST);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glDepthMask(GL_TRUE);
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilMask(0xFF);
auto makeColor = [](GLsizei row, GLsizei col) -> GLColor {
return GLColor(row * 255 / kHeight, col * 255 / kWidth, (row * 7 + col * 11) % 256, 255);
};
auto makeDepth = [](GLsizei row, GLsizei col) -> float {
return 1.8f * ((row * kWidth + col) % 33 / 32.0f) - 0.9f;
};
auto makeStencil = [](GLsizei row, GLsizei col) -> uint8_t {
return (col * kHeight + row) & 0xFF;
};
glBindFramebuffer(GL_FRAMEBUFFER, srcFBO);
glUseProgram(drawColor);
for (GLsizei row = 0; row < kHeight; ++row)
{
for (GLsizei col = 0; col < kWidth; ++col)
{
glScissor(col, row, 1, 1);
glUniform4fv(colorLoc, 1, makeColor(row, col).toNormalizedVector().data());
glStencilFunc(GL_ALWAYS, makeStencil(row, col), 0xFF);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), makeDepth(row, col));
}
}
glDepthFunc(GL_LESS);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
auto test = [&](int testIndex, bool flipX, bool flipY, GLint srcOffsetX, GLint srcOffsetY,
GLint dstOffsetX, GLint dstOffsetY, GLint width, GLint height) {
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
const GLint srcX0 = srcOffsetX;
const GLint srcY0 = srcOffsetY;
const GLint srcX1 = srcOffsetX + width;
const GLint srcY1 = srcOffsetY + height;
const GLint dstX0 = flipX ? dstOffsetX + width : dstOffsetX;
const GLint dstY0 = flipY ? dstOffsetY + height : dstOffsetY;
const GLint dstX1 = flipX ? dstOffsetX : dstOffsetX + width;
const GLint dstY1 = flipY ? dstOffsetY : dstOffsetY + height;
glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
GL_NEAREST);
// Verify results
glBindFramebuffer(GL_READ_FRAMEBUFFER, dstFBO);
for (GLsizei row = 0; row < height; ++row)
{
for (GLsizei col = 0; col < width; ++col)
{
const GLint srcPixelX = col + srcOffsetX;
const GLint srcPixelY = row + srcOffsetY;
const GLint dstPixelX = dstOffsetX + (flipX ? width - 1 - col : col);
const GLint dstPixelY = dstOffsetY + (flipY ? height - 1 - row : row);
const GLColor expectColor = makeColor(srcPixelY, srcPixelX);
const float expectDepth = makeDepth(srcPixelY, srcPixelX);
const uint8_t expectStencil = makeStencil(srcPixelY, srcPixelX);
// Verify color
EXPECT_PIXEL_COLOR_EQ(dstPixelX, dstPixelY, expectColor)
<< testIndex << " " << flipX << " " << flipY << " " << row << " " << col;
glEnable(GL_SCISSOR_TEST);
glScissor(dstPixelX, dstPixelY, 1, 1);
// Verify depth and stencil
glStencilFunc(GL_EQUAL, expectStencil, 0xFF);
drawQuad(drawRed, essl1_shaders::PositionAttrib(), expectDepth - 0.05);
drawQuad(drawGreen, essl1_shaders::PositionAttrib(), expectDepth + 0.05);
EXPECT_PIXEL_COLOR_EQ(dstPixelX, dstPixelY, GLColor::red)
<< testIndex << " " << flipX << " " << flipY << " " << row << " " << col;
}
}
};
for (int flipX = 0; flipX < 2; ++flipX)
{
for (int flipY = 0; flipY < 2; ++flipY)
{
// Test 0, full sized blit
test(0, flipX != 0, flipY != 0, 0, 0, 0, 0, kWidth, kHeight);
// Test 1, blit only one pixel
test(1, flipX != 0, flipY != 0, kWidth / 3, kHeight / 7, 2 * kWidth / 5,
3 * kHeight / 4, 1, 1);
// Test 2, random region
test(2, flipX != 0, flipY != 0, kWidth / 5, 2 * kHeight / 7, kWidth / 6, kHeight / 4,
kWidth / 2, kHeight / 2);
}
}
}
// Test blitting a depthStencil buffer with multiple depth values to a larger size.
TEST_P(BlitFramebufferTest, BlitDepthStencilPixelByPixel)
{
BlitDepthStencilPixelByPixelTestHelper(false /* mesaYFlip */);
}
// Same as BlitDepthStencilPixelByPixel, but with y-flip flag set.
TEST_P(BlitFramebufferTest, BlitDepthStencilPixelByPixelMesaYFlip)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_MESA_framebuffer_flip_y"));
BlitDepthStencilPixelByPixelTestHelper(true /* mesaYFlip */);
}
// Regression test for a bug in the Vulkan backend where vkCmdResolveImage was used with
// out-of-bounds regions.
TEST_P(BlitFramebufferTestES31, OOBResolve)
{
constexpr GLint kWidth = 16;
constexpr GLint kHeight = 32;
// Read framebuffer is multisampled.
GLTexture readTexture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, readTexture);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kWidth, kHeight, GL_TRUE);
GLFramebuffer readFbo;
glBindFramebuffer(GL_FRAMEBUFFER, readFbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
readTexture, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Draw framebuffer is single sampled.
GLTexture drawTexture;
glBindTexture(GL_TEXTURE_2D, drawTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glGenerateMipmap(GL_TEXTURE_2D);
GLFramebuffer drawFbo;
glBindFramebuffer(GL_FRAMEBUFFER, drawFbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, drawTexture, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::green);
// Resolve the read framebuffer, using bounds that are outside the size of the image.
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
glBlitFramebuffer(-kWidth * 2, -kHeight * 3, kWidth * 11, kHeight * 8, -kWidth * 2,
-kHeight * 3, kWidth * 11, kHeight * 8, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, drawFbo);
EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
}
// Regression test for a bug in the Vulkan backend where vkCmdResolveImage was using the src extents
// as the resolve area instead of the area passed to glBlitFramebuffer.
TEST_P(BlitFramebufferTestES31, PartialResolve)
{
constexpr GLint kWidth = 16;
constexpr GLint kHeight = 32;
// Read framebuffer is multisampled.
GLTexture readTexture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, readTexture);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kWidth, kHeight, GL_TRUE);
GLFramebuffer readFbo;
glBindFramebuffer(GL_FRAMEBUFFER, readFbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
readTexture, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Draw framebuffer is single sampled. It's bound to a texture with base level the same size as
// the read framebuffer, but it's bound to mip 1.
GLTexture drawTexture;
glBindTexture(GL_TEXTURE_2D, drawTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glGenerateMipmap(GL_TEXTURE_2D);
GLFramebuffer drawFbo;
glBindFramebuffer(GL_FRAMEBUFFER, drawFbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, drawTexture, 1);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_RECT_EQ(0, 0, kWidth / 2, kHeight / 2, GLColor::green);
constexpr GLint kResolveX0 = 1;
constexpr GLint kResolveY0 = 2;
constexpr GLint kResolveX1 = 4;
constexpr GLint kResolveY1 = 6;
// Resolve only a portion of the read framebuffer.
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
glBlitFramebuffer(kResolveX0, kResolveY0, kResolveX1, kResolveY1, kResolveX0, kResolveY0,
kResolveX1, kResolveY1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, drawFbo);
EXPECT_PIXEL_RECT_EQ(0, 0, kWidth / 2, kResolveY0, GLColor::green);
EXPECT_PIXEL_RECT_EQ(0, 0, kResolveX0, kHeight / 2, GLColor::green);
EXPECT_PIXEL_RECT_EQ(kResolveX1, 0, kWidth / 2 - kResolveX1, kHeight / 2, GLColor::green);
EXPECT_PIXEL_RECT_EQ(0, kResolveY1, kWidth / 2, kHeight / 2 - kResolveY1, GLColor::green);
EXPECT_PIXEL_RECT_EQ(kResolveX0, kResolveY0, kResolveX1 - kResolveX0, kResolveY1 - kResolveY0,
GLColor::red);
}
// Test that a draw call to a small FBO followed by a resolve of a large FBO works.
TEST_P(BlitFramebufferTestES31, DrawToSmallFBOThenResolveLargeFBO)
{
GLFramebuffer fboMS[2];
GLTexture textureMS[2];
GLFramebuffer fboSS;
GLTexture textureSS;
// A bug in the Vulkan backend grew the render area of the previous render pass on blit, even
// though the previous render pass belonged to an unrelated framebuffer. This test only needs
// to make sure that the FBO being resolved is not strictly smaller than the previous FBO which
// was drawn to.
constexpr GLsizei kLargeWidth = 127;
constexpr GLsizei kLargeHeight = 54;
constexpr GLsizei kSmallWidth = 37;
constexpr GLsizei kSmallHeight = 79;
ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
// Create resolve target.
glBindTexture(GL_TEXTURE_2D, textureSS);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kLargeWidth, kLargeHeight);
glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureSS, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Create multisampled framebuffers and draw into them one by one.
for (size_t fboIndex = 0; fboIndex < 2; ++fboIndex)
{
const GLsizei width = fboIndex == 0 ? kLargeWidth : kSmallWidth;
const GLsizei height = fboIndex == 0 ? kLargeHeight : kSmallHeight;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureMS[fboIndex]);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, width, height, GL_TRUE);
glBindFramebuffer(GL_FRAMEBUFFER, fboMS[fboIndex]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
textureMS[fboIndex], 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glViewport(0, 0, width, height);
drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.8f);
EXPECT_GL_NO_ERROR();
}
// Resolve the first FBO
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboSS);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS[0]);
glViewport(0, 0, kLargeWidth, kLargeHeight);
glBlitFramebuffer(0, 0, kLargeWidth, kLargeHeight, 0, 0, kLargeWidth, kLargeHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
// Verify the resolve
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSS);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kLargeWidth - 1, kLargeHeight - 1, GLColor::red);
}
// Blit a multisampled RGBX8 framebuffer to an RGB8 framebuffer.
TEST_P(BlitFramebufferTestES31, BlitMultisampledRGBX8ToRGB8)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_rgbx_internal_format"));
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLTexture textureMS;
GLRenderbuffer targetRBO;
GLFramebuffer sourceFBO, targetFBO;
// Initialize a source multisampled FBO with checker pattern
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureMS);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBX8_ANGLE, kWidth, kHeight,
GL_TRUE);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, sourceFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
textureMS, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ANGLE_GL_PROGRAM(checkerProgram, essl1_shaders::vs::Passthrough(),
essl1_shaders::fs::Checkered());
glViewport(0, 0, kWidth, kHeight);
glBindFramebuffer(GL_FRAMEBUFFER, sourceFBO);
drawQuad(checkerProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
// Initialize the destination FBO
initColorFBO(&targetFBO, &targetRBO, GL_RGB8, kWidth, kHeight);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Scale down without flipping.
glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::blue);
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);
glFramebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA, 1);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
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,
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);
// 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);
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,
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);
// 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);
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,
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);
// 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);
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,
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);
}
// Test resolving into smaller framebuffer.
TEST_P(BlitFramebufferTest, ResolveIntoSmallerFramebuffer)
{
constexpr GLuint kSize[2] = {40, 32};
glViewport(0, 0, kSize[0], kSize[0]);
GLRenderbuffer rbo[2];
GLFramebuffer fbo[2];
for (int i = 0; i < 2; ++i)
{
glBindRenderbuffer(GL_RENDERBUFFER, rbo[i]);
if (i == 0)
{
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kSize[i], kSize[i]);
}
else
{
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kSize[i], kSize[i]);
}
glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo[i]);
}
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.3f);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
glBlitFramebuffer(0, 0, kSize[1], kSize[1], 0, 0, kSize[1], kSize[1], GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]);
EXPECT_PIXEL_RECT_EQ(0, 0, kSize[1], kSize[1], GLColor::red);
}
// Test resolving into bigger framebuffer.
TEST_P(BlitFramebufferTest, ResolveIntoBiggerFramebuffer)
{
constexpr GLuint kSize[2] = {32, 40};
glViewport(0, 0, kSize[0], kSize[0]);
GLRenderbuffer rbo[2];
GLFramebuffer fbo[2];
for (int i = 0; i < 2; ++i)
{
glBindRenderbuffer(GL_RENDERBUFFER, rbo[i]);
if (i == 0)
{
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kSize[i], kSize[i]);
}
else
{
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kSize[i], kSize[i]);
}
glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo[i]);
}
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.3f);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
glBlitFramebuffer(0, 0, kSize[1], kSize[1], 0, 0, kSize[1], kSize[1], GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]);
EXPECT_PIXEL_RECT_EQ(0, 0, kSize[0], kSize[0], GLColor::red);
}
// Test resolving into a rotated framebuffer
TEST_P(BlitFramebufferTest, ResolveWithRotation)
{
const GLint w = getWindowWidth();
const GLint h = getWindowHeight();
glViewport(0, 0, w, h);
GLRenderbuffer rbo;
GLFramebuffer fbo;
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, w, h);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Checkered());
glUseProgram(program);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.3f);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
EXPECT_PIXEL_RECT_EQ(0, 0, w / 2, h / 2, GLColor::red);
EXPECT_PIXEL_RECT_EQ(w / 2, 0, w / 2, h / 2, GLColor::blue);
EXPECT_PIXEL_RECT_EQ(0, h / 2, w / 2, h / 2, GLColor::green);
EXPECT_PIXEL_RECT_EQ(w / 2, h / 2, w / 2, h / 2, GLColor::yellow);
}
// Test blitting a 3D texture to a 3D texture
TEST_P(BlitFramebufferTest, Blit3D)
{
test3DBlit(GL_TEXTURE_3D, GL_TEXTURE_3D);
}
// Test blitting a 2D array texture to a 2D array texture
TEST_P(BlitFramebufferTest, Blit2DArray)
{
test3DBlit(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_2D_ARRAY);
}
// Test blitting a 3D texture to a 2D array texture
TEST_P(BlitFramebufferTest, Blit3DTo2DArray)
{
test3DBlit(GL_TEXTURE_3D, GL_TEXTURE_2D_ARRAY);
}
// Test blitting a 2D array texture to a 3D texture
TEST_P(BlitFramebufferTest, Blit2DArrayTo3D)
{
test3DBlit(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D);
}
// 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);
ANGLE_INSTANTIATE_TEST(BlitFramebufferANGLETest,
ES2_D3D9(),
ES2_D3D11(),
ES2_D3D11_PRESENT_PATH_FAST(),
ES2_OPENGL(),
ES3_OPENGL(),
ES2_VULKAN(),
ES3_VULKAN(),
ES3_VULKAN().enable(Feature::EmulatedPrerotation90),
ES3_VULKAN().enable(Feature::EmulatedPrerotation180),
ES3_VULKAN().enable(Feature::EmulatedPrerotation270),
ES3_VULKAN()
.disable(Feature::SupportsExtendedDynamicState)
.disable(Feature::SupportsExtendedDynamicState2),
ES3_VULKAN().disable(Feature::SupportsExtendedDynamicState2),
ES2_METAL(),
ES2_METAL().disable(Feature::HasShaderStencilOutput));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BlitFramebufferTest);
ANGLE_INSTANTIATE_TEST_ES3_AND(BlitFramebufferTest,
ES3_VULKAN().enable(Feature::EmulatedPrerotation90),
ES3_VULKAN().enable(Feature::EmulatedPrerotation180),
ES3_VULKAN().enable(Feature::EmulatedPrerotation270),
ES3_VULKAN()
.disable(Feature::SupportsExtendedDynamicState)
.disable(Feature::SupportsExtendedDynamicState2),
ES3_VULKAN().disable(Feature::SupportsExtendedDynamicState2),
ES3_VULKAN().enable(Feature::DisableFlippingBlitWithCommand),
ES3_METAL().disable(Feature::HasShaderStencilOutput));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BlitFramebufferTestES31);
ANGLE_INSTANTIATE_TEST_ES31(BlitFramebufferTestES31);
} // namespace