blob: 6ed4f8fbc4c13fd4a5a5df84c1c6d31ded9cdf15 [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.
//
// Framebuffer tests:
// Various tests related for Frambuffers.
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
class FramebufferFormatsTest : public ANGLETest
{
protected:
FramebufferFormatsTest() : mFramebuffer(0), mTexture(0), mRenderbuffer(0), mProgram(0)
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
void checkBitCount(GLuint fbo, GLenum channel, GLint minBits)
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLint bits = 0;
glGetIntegerv(channel, &bits);
if (minBits == 0)
{
EXPECT_EQ(minBits, bits);
}
else
{
EXPECT_GE(bits, minBits);
}
}
void testBitCounts(GLuint fbo,
GLint minRedBits,
GLint minGreenBits,
GLint minBlueBits,
GLint minAlphaBits,
GLint minDepthBits,
GLint minStencilBits)
{
checkBitCount(fbo, GL_RED_BITS, minRedBits);
checkBitCount(fbo, GL_GREEN_BITS, minGreenBits);
checkBitCount(fbo, GL_BLUE_BITS, minBlueBits);
checkBitCount(fbo, GL_ALPHA_BITS, minAlphaBits);
checkBitCount(fbo, GL_DEPTH_BITS, minDepthBits);
checkBitCount(fbo, GL_STENCIL_BITS, minStencilBits);
}
void testTextureFormat(GLenum internalFormat,
GLint minRedBits,
GLint minGreenBits,
GLint minBlueBits,
GLint minAlphaBits)
{
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture);
if (getClientMajorVersion() >= 3)
{
glTexStorage2D(GL_TEXTURE_2D, 1, internalFormat, 1, 1);
}
else
{
glTexStorage2DEXT(GL_TEXTURE_2D, 1, internalFormat, 1, 1);
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
testBitCounts(mFramebuffer, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0);
}
void testRenderbufferMultisampleFormat(int minESVersion,
GLenum attachmentType,
GLenum internalFormat)
{
// TODO(geofflang): Figure out why this is broken on Intel OpenGL
if (IsIntel() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
{
std::cout << "Test skipped on Intel OpenGL." << std::endl;
return;
}
int clientVersion = getClientMajorVersion();
if (clientVersion < minESVersion)
{
return;
}
// Check that multisample is supported with at least two samples (minimum required is 1)
bool supports2Samples = false;
if (clientVersion == 2)
{
if (extensionEnabled("ANGLE_framebuffer_multisample"))
{
int maxSamples;
glGetIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSamples);
supports2Samples = maxSamples >= 2;
}
}
else
{
assert(clientVersion >= 3);
int maxSamples;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
supports2Samples = maxSamples >= 2;
}
if (!supports2Samples)
{
return;
}
glGenRenderbuffers(1, &mRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
EXPECT_GL_NO_ERROR();
glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, internalFormat, 128, 128);
EXPECT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentType, GL_RENDERBUFFER, mRenderbuffer);
EXPECT_GL_NO_ERROR();
}
void testZeroHeightRenderbuffer()
{
glGenRenderbuffers(1, &mRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
mRenderbuffer);
EXPECT_GL_NO_ERROR();
}
void SetUp() override
{
ANGLETest::SetUp();
glGenFramebuffers(1, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
}
void TearDown() override
{
ANGLETest::TearDown();
if (mTexture != 0)
{
glDeleteTextures(1, &mTexture);
mTexture = 0;
}
if (mRenderbuffer != 0)
{
glDeleteRenderbuffers(1, &mRenderbuffer);
mRenderbuffer = 0;
}
if (mFramebuffer != 0)
{
glDeleteFramebuffers(1, &mFramebuffer);
mFramebuffer = 0;
}
if (mProgram != 0)
{
glDeleteProgram(mProgram);
mProgram = 0;
}
}
GLuint mFramebuffer;
GLuint mTexture;
GLuint mRenderbuffer;
GLuint mProgram;
};
TEST_P(FramebufferFormatsTest, RGBA4)
{
if (getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_texture_storage"))
{
std::cout << "Test skipped due to missing ES3 or GL_EXT_texture_storage." << std::endl;
return;
}
testTextureFormat(GL_RGBA4, 4, 4, 4, 4);
}
TEST_P(FramebufferFormatsTest, RGB565)
{
if (getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_texture_storage"))
{
std::cout << "Test skipped due to missing ES3 or GL_EXT_texture_storage." << std::endl;
return;
}
testTextureFormat(GL_RGB565, 5, 6, 5, 0);
}
TEST_P(FramebufferFormatsTest, RGB8)
{
if (getClientMajorVersion() < 3 &&
(!extensionEnabled("GL_OES_rgb8_rgba8") || !extensionEnabled("GL_EXT_texture_storage")))
{
std::cout
<< "Test skipped due to missing ES3 or GL_OES_rgb8_rgba8 and GL_EXT_texture_storage."
<< std::endl;
return;
}
testTextureFormat(GL_RGB8_OES, 8, 8, 8, 0);
}
TEST_P(FramebufferFormatsTest, BGRA8)
{
if (!extensionEnabled("GL_EXT_texture_format_BGRA8888") ||
(getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_texture_storage")))
{
std::cout << "Test skipped due to missing GL_EXT_texture_format_BGRA8888 or "
"GL_EXT_texture_storage."
<< std::endl;
return;
}
testTextureFormat(GL_BGRA8_EXT, 8, 8, 8, 8);
}
TEST_P(FramebufferFormatsTest, RGBA8)
{
if (getClientMajorVersion() < 3 &&
(!extensionEnabled("GL_OES_rgb8_rgba8") || !extensionEnabled("GL_EXT_texture_storage")))
{
std::cout
<< "Test skipped due to missing ES3 or GL_OES_rgb8_rgba8 and GL_EXT_texture_storage."
<< std::endl;
return;
}
testTextureFormat(GL_RGBA8_OES, 8, 8, 8, 8);
}
TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH16)
{
testRenderbufferMultisampleFormat(2, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT16);
}
TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH24)
{
testRenderbufferMultisampleFormat(3, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT24);
}
TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH32F)
{
if (getClientMajorVersion() < 3)
{
std::cout << "Test skipped due to missing ES3." << std::endl;
return;
}
testRenderbufferMultisampleFormat(3, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT32F);
}
TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH24_STENCIL8)
{
testRenderbufferMultisampleFormat(3, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH24_STENCIL8);
}
TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH32F_STENCIL8)
{
if (getClientMajorVersion() < 3)
{
std::cout << "Test skipped due to missing ES3." << std::endl;
return;
}
testRenderbufferMultisampleFormat(3, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH32F_STENCIL8);
}
TEST_P(FramebufferFormatsTest, RenderbufferMultisample_STENCIL_INDEX8)
{
// TODO(geofflang): Figure out how to support GLSTENCIL_INDEX8 on desktop GL
if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
{
std::cout << "Test skipped on Desktop OpenGL." << std::endl;
return;
}
testRenderbufferMultisampleFormat(2, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX8);
}
// Test that binding an incomplete cube map is rejected by ANGLE.
TEST_P(FramebufferFormatsTest, IncompleteCubeMap)
{
// First make a complete CubeMap.
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, mTexture);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
mTexture, 0);
// Verify the framebuffer is complete.
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Make the CubeMap cube-incomplete.
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
// Verify the framebuffer is incomplete.
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Verify drawing with the incomplete framebuffer produces a GL error
const std::string &vs = "attribute vec4 position; void main() { gl_Position = position; }";
const std::string &ps = "void main() { gl_FragColor = vec4(1, 0, 0, 1); }";
mProgram = CompileProgram(vs, ps);
ASSERT_NE(0u, mProgram);
drawQuad(mProgram, "position", 0.5f);
ASSERT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
}
// Test that a renderbuffer with zero height but nonzero width is handled without crashes/asserts.
TEST_P(FramebufferFormatsTest, ZeroHeightRenderbuffer)
{
if (getClientMajorVersion() < 3)
{
std::cout << "Test skipped due to missing ES3" << std::endl;
return;
}
testZeroHeightRenderbuffer();
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest,
ES2_D3D9(),
ES2_D3D11(),
ES3_D3D11(),
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES());
class FramebufferTest_ES3 : public ANGLETest
{
protected:
FramebufferTest_ES3() : mFramebuffer(0), mRenderbuffer(0) {}
void SetUp() override
{
ANGLETest::SetUp();
glGenFramebuffers(1, &mFramebuffer);
glGenRenderbuffers(1, &mRenderbuffer);
}
void TearDown() override
{
glDeleteFramebuffers(1, &mFramebuffer);
glDeleteRenderbuffers(1, &mRenderbuffer);
ANGLETest::TearDown();
}
GLuint mFramebuffer;
GLuint mRenderbuffer;
};
// Covers invalidating an incomplete framebuffer. This should be a no-op, but should not error.
TEST_P(FramebufferTest_ES3, InvalidateIncomplete)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
std::vector<GLenum> attachments;
attachments.push_back(GL_COLOR_ATTACHMENT0);
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments.data());
EXPECT_GL_NO_ERROR();
}
// Test that the framebuffer state tracking robustly handles a depth-only attachment being set
// as a depth-stencil attachment. It is equivalent to detaching the depth-stencil attachment.
TEST_P(FramebufferTest_ES3, DepthOnlyAsDepthStencil)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 4, 4);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
mRenderbuffer);
EXPECT_GLENUM_NE(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
ANGLE_INSTANTIATE_TEST(FramebufferTest_ES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
class FramebufferTest_ES31 : public ANGLETest
{
};
// Test that without attachment, if either the value of FRAMEBUFFER_DEFAULT_WIDTH or
// FRAMEBUFFER_DEFAULT_HEIGHT parameters is zero, the framebuffer is incomplete.
TEST_P(FramebufferTest_ES31, IncompleteMissingAttachmentDefaultParam)
{
GLFramebuffer mFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer.get());
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 1);
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, 1);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 0);
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 1);
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 0);
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, 1);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that the sample count of a mix of texture and renderbuffer should be same.
TEST_P(FramebufferTest_ES31, IncompleteMultisampleSampleCountMix)
{
GLFramebuffer mFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer.get());
GLTexture mTexture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mTexture.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, 1, 1, true);
GLRenderbuffer mRenderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer.get());
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_RGBA8, 1, 1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
mTexture.get(), 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER,
mRenderbuffer.get());
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that the sample count of texture attachments should be same.
TEST_P(FramebufferTest_ES31, IncompleteMultisampleSampleCountTex)
{
GLFramebuffer mFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer.get());
GLTexture mTextures[2];
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mTextures[0].get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, 1, 1, true);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mTextures[1].get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 2, GL_RGBA8, 1, 1, true);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
mTextures[0].get(), 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE,
mTextures[1].get(), 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that if the attached images are a mix of renderbuffers and textures, the value of
// TEXTURE_FIXED_SAMPLE_LOCATIONS must be TRUE for all attached textures.
TEST_P(FramebufferTest_ES31, IncompleteMultisampleFixedSampleLocationsMix)
{
GLFramebuffer mFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer.get());
GLTexture mTexture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mTexture.get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, 1, 1, false);
GLRenderbuffer mRenderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer.get());
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 1, GL_RGBA8, 1, 1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
mTexture.get(), 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER,
mRenderbuffer.get());
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
// Test that the value of TEXTURE_FIXED_SAMPLE_LOCATIONS is the same for all attached textures.
TEST_P(FramebufferTest_ES31, IncompleteMultisampleFixedSampleLocationsTex)
{
GLFramebuffer mFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer.get());
GLTexture mTextures[2];
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mTextures[0].get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, 1, 1, false);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
mTextures[0].get(), 0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mTextures[1].get());
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGB8, 1, 1, true);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE,
mTextures[1].get(), 0);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST(FramebufferTest_ES31, ES31_OPENGL(), ES31_OPENGLES());