blob: ac3cedeaa18dc6195b42a2fe423b250859ace5a6 [file]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES2/gl2extchromium.h>
#include <GLES3/gl3.h>
#include <stdint.h>
#include "base/containers/heap_array.h"
#include "build/build_config.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/tests/gl_manager.h"
#include "gpu/command_buffer/tests/gl_test_service_helper.h"
#include "gpu/command_buffer/tests/gl_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/buildflags.h"
namespace gpu {
class GLTest : public testing::Test {
protected:
void SetUp() override { gl_.Initialize(GLManager::Options()); }
void TearDown() override { gl_.Destroy(); }
GLManager gl_;
};
// Test that GL is at least minimally working.
TEST_F(GLTest, Basic) {
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
uint8_t expected[] = {
0, 255, 0, 255,
};
EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0, expected, nullptr));
GLTestHelper::CheckGLError("no errors", __LINE__);
}
TEST_F(GLTest, BasicFBO) {
GLuint tex = 0;
glGenTextures(1, &tex);
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindTexture(GL_TEXTURE_2D, tex);
auto pixels = base::HeapArray<uint8_t>::WithSize(16 * 16 * 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
pixels.data());
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
tex, 0);
EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
uint8_t expected[] = {
0, 255, 0, 255,
};
EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 16, 16, 0, expected, nullptr));
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &tex);
GLTestHelper::CheckGLError("no errors", __LINE__);
}
TEST_F(GLTest, SimpleShader) {
static const char* v_shader_str =
"attribute vec4 a_Position;\n"
"void main()\n"
"{\n"
" gl_Position = a_Position;\n"
"}\n";
static const char* f_shader_str =
"precision mediump float;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
"}\n";
GLuint program = GLTestHelper::LoadProgram(v_shader_str, f_shader_str);
glUseProgram(program);
GLuint position_loc = glGetAttribLocation(program, "a_Position");
GLTestHelper::SetupUnitQuad(position_loc);
uint8_t expected_clear[] = {
127, 0, 255, 0,
};
glClearColor(0.5f, 0.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_TRUE(
GLTestHelper::CheckPixels(0, 0, 1, 1, 1, expected_clear, nullptr));
uint8_t expected_draw[] = {
0, 255, 0, 255,
};
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0, expected_draw, nullptr));
}
TEST_F(GLTest, FeatureFlagsMatchCapabilities) {
scoped_refptr<gles2::FeatureInfo> features =
new gles2::FeatureInfo(gl_.workarounds(), GpuFeatureInfo());
features->InitializeForTesting();
const auto& caps = gl_.GetCapabilities();
const auto& gl_caps = gl_.GetGLCapabilities();
const auto& flags = features->feature_flags();
EXPECT_EQ(caps.egl_image_external, flags.oes_egl_image_external);
EXPECT_EQ(caps.texture_format_bgra8888, flags.ext_texture_format_bgra8888);
EXPECT_EQ(gl_caps.sync_query, flags.chromium_sync_query);
EXPECT_EQ(caps.sync_query, flags.chromium_sync_query);
EXPECT_EQ(caps.texture_rg, flags.ext_texture_rg);
EXPECT_EQ(caps.image_ar30, flags.chromium_image_ar30);
EXPECT_EQ(caps.image_ab30, flags.chromium_image_ab30);
EXPECT_EQ(caps.render_buffer_format_bgra8888,
flags.ext_render_buffer_format_bgra8888);
EXPECT_EQ(gl_caps.occlusion_query_boolean, flags.occlusion_query_boolean);
}
TEST_F(GLTest, GetString) {
EXPECT_STREQ(
"OpenGL ES 2.0 Chromium",
reinterpret_cast<const char*>(glGetString(GL_VERSION)));
EXPECT_STREQ(
"OpenGL ES GLSL ES 1.0 Chromium",
reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
}
#if BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER)
class GL3Test : public GLTest {
protected:
void SetUp() override {
if (gl_.gpu_preferences().use_passthrough_cmd_decoder) {
GTEST_SKIP() << "Test only applies to validating decoder";
}
GLManager::Options options;
options.context_type = CONTEXT_TYPE_OPENGLES3;
gl_.Initialize(options);
}
};
// Tests that glCopyTexImage2D correctly updates the internal service-side
// texture level size when using the workaround path that temporarily clamps
// base/max levels to prevent FBO incompleteness. Specifically, it verifies
// that the workaround uses the correct base target (GL_TEXTURE_CUBE_MAP)
// instead of the specific face target (e.g., GL_TEXTURE_CUBE_MAP_POSITIVE_X)
// which would cause driver-side GL_INVALID_ENUM errors and result in a state
// desynchronization between the decoder and the GPU driver.
//
// TODO(crbug.com/513543143): Goldfish GLES emulator driver on 32-bit x86
// Android bots has a known driver bug where it incorrectly rejects
// glCopyTexImage2D on cubemaps with GL_INVALID_ENUM.
#if !(BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86))
TEST_F(GL3Test, CopyTexImage2DCubeMapStateDesync) {
GLuint tex = 0;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
// We manually specify all mip levels instead of calling glGenerateMipmap
// because various GPU drivers (e.g., the Android x64 emulator) have bugs
// when generating cubemap texture mipmaps, which can cause FBO
// incompleteness.
auto pixels = base::HeapArray<uint8_t>::WithSize(32 * 32 * 4);
for (int level = 0; level < 6; ++level) {
int size = 32 >> level;
for (int i = 0; i < 6; ++i) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level, GL_RGBA8, size,
size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
}
}
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, tex, 3);
EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
glCheckFramebufferStatus(GL_FRAMEBUFFER));
glCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 5, GL_RGBA8, 0, 0, 2, 2, 0);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
// Verify that the decoder's internal LevelInfo tracking has been correctly
// updated to match the new 2x2 size. If the workaround had triggered a driver
// error, the decoder would have skipped updating this state while the driver-
// side allocation would still be updated, leading to a security
// vulnerability.
int tracked_width = 0;
int tracked_height = 0;
bool defined =
InspectTextureLevelSize(&gl_, tex, GL_TEXTURE_CUBE_MAP_POSITIVE_X, 5,
&tracked_width, &tracked_height);
EXPECT_TRUE(defined);
EXPECT_EQ(2, tracked_width);
EXPECT_EQ(2, tracked_height);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &tex);
}
#endif // !(BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86))
class GL3MultisampleCopyTexImageTest : public GL3Test {
protected:
void SetUp() override { GL3Test::SetUp(); }
void TearDown() override {
if (sample_fbo_) {
glDeleteFramebuffers(1, &sample_fbo_);
}
if (sample_tex_) {
glDeleteTextures(1, &sample_tex_);
}
if (sample_rb_) {
glDeleteRenderbuffers(1, &sample_rb_);
}
if (dest_tex_) {
glDeleteTextures(1, &dest_tex_);
}
GL3Test::TearDown();
}
void SetUpImplicitResolveTextureFBO() {
glGenTextures(1, &sample_tex_);
glBindTexture(GL_TEXTURE_2D, sample_tex_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glGenFramebuffers(1, &sample_fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, sample_fbo_);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, sample_tex_, 0, 4);
EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
glCheckFramebufferStatus(GL_FRAMEBUFFER));
GLTestHelper::CheckGLError("SetUpImplicitResolveTextureFBO", __LINE__);
}
void SetUpMultisampleRenderbufferFBO(GLsizei samples = 4) {
glGenRenderbuffers(1, &sample_rb_);
glBindRenderbuffer(GL_RENDERBUFFER, sample_rb_);
glRenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, samples, GL_RGBA8,
16, 16);
glGenFramebuffers(1, &sample_fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, sample_fbo_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, sample_rb_);
EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
glCheckFramebufferStatus(GL_FRAMEBUFFER));
GLTestHelper::CheckGLError("SetUpMultisampleRenderbufferFBO", __LINE__);
}
void SetUpImplicitResolveRenderbufferFBO() {
glGenRenderbuffers(1, &sample_rb_);
glBindRenderbuffer(GL_RENDERBUFFER, sample_rb_);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_RGBA8, 16, 16);
glGenFramebuffers(1, &sample_fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, sample_fbo_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, sample_rb_);
EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
glCheckFramebufferStatus(GL_FRAMEBUFFER));
GLTestHelper::CheckGLError("SetUpImplicitResolveRenderbufferFBO", __LINE__);
}
void SetUpDestTexture2D() {
glGenTextures(1, &dest_tex_);
glBindTexture(GL_TEXTURE_2D, dest_tex_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
GLTestHelper::CheckGLError("SetUpDestTexture2D", __LINE__);
}
void SetUpDestTexture3D() {
glGenTextures(1, &dest_tex_);
glBindTexture(GL_TEXTURE_3D, dest_tex_);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 16, 16, 16, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
GLTestHelper::CheckGLError("SetUpDestTexture3D", __LINE__);
}
void VerifyCopySucceeded(GLenum dest_target,
int expected_width,
int expected_height) {
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
VerifyTextureSize(dest_target, expected_width, expected_height);
}
void VerifyCopyBlocked(GLenum dest_target) {
EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
VerifyTextureSize(dest_target, 16, 16);
}
void VerifyTextureSize(GLenum target,
int expected_width,
int expected_height) {
int tracked_width = 0;
int tracked_height = 0;
bool defined = InspectTextureLevelSize(&gl_, dest_tex_, target, 0,
&tracked_width, &tracked_height);
EXPECT_TRUE(defined);
EXPECT_EQ(expected_width, tracked_width);
EXPECT_EQ(expected_height, tracked_height);
}
GLuint sample_fbo_ = 0;
GLuint sample_tex_ = 0;
GLuint sample_rb_ = 0;
GLuint dest_tex_ = 0;
};
// Implicit Resolve Texture
TEST_F(GL3MultisampleCopyTexImageTest, CopyTexImage2DImplicitResolveTexture) {
if (!GLTestHelper::HasExtension("GL_EXT_multisample_render_to_texture")) {
GTEST_SKIP() << "GL_EXT_multisample_render_to_texture not supported";
}
SetUpImplicitResolveTextureFBO();
SetUpDestTexture2D();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 2, 2, 0);
VerifyCopySucceeded(GL_TEXTURE_2D, 2, 2);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage2DImplicitResolveTexture) {
if (!GLTestHelper::HasExtension("GL_EXT_multisample_render_to_texture")) {
GTEST_SKIP() << "GL_EXT_multisample_render_to_texture not supported";
}
SetUpImplicitResolveTextureFBO();
SetUpDestTexture2D();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 2, 2);
VerifyCopySucceeded(GL_TEXTURE_2D, 16, 16);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage3DImplicitResolveTexture) {
if (!GLTestHelper::HasExtension("GL_EXT_multisample_render_to_texture")) {
GTEST_SKIP() << "GL_EXT_multisample_render_to_texture not supported";
}
SetUpImplicitResolveTextureFBO();
SetUpDestTexture3D();
glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
VerifyCopySucceeded(GL_TEXTURE_3D, 16, 16);
}
// Multisample Renderbuffer
TEST_F(GL3MultisampleCopyTexImageTest, CopyTexImage2DMultisampleRenderbuffer) {
if (!GLTestHelper::HasExtension("GL_CHROMIUM_framebuffer_multisample")) {
GTEST_SKIP() << "GL_CHROMIUM_framebuffer_multisample not supported";
}
SetUpMultisampleRenderbufferFBO();
SetUpDestTexture2D();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 2, 2, 0);
VerifyCopyBlocked(GL_TEXTURE_2D);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage2DMultisampleRenderbuffer) {
if (!GLTestHelper::HasExtension("GL_CHROMIUM_framebuffer_multisample")) {
GTEST_SKIP() << "GL_CHROMIUM_framebuffer_multisample not supported";
}
SetUpMultisampleRenderbufferFBO();
SetUpDestTexture2D();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 2, 2);
VerifyCopyBlocked(GL_TEXTURE_2D);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage3DMultisampleRenderbuffer) {
if (!GLTestHelper::HasExtension("GL_CHROMIUM_framebuffer_multisample")) {
GTEST_SKIP() << "GL_CHROMIUM_framebuffer_multisample not supported";
}
SetUpMultisampleRenderbufferFBO();
SetUpDestTexture3D();
glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
VerifyCopyBlocked(GL_TEXTURE_3D);
}
// Multisample Renderbuffer with Sample Count 1
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexImage2DMultisampleRenderbufferSampleCount1) {
if (!GLTestHelper::HasExtension("GL_CHROMIUM_framebuffer_multisample")) {
GTEST_SKIP() << "GL_CHROMIUM_framebuffer_multisample not supported";
}
SetUpMultisampleRenderbufferFBO(/*samples=*/1);
SetUpDestTexture2D();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 2, 2, 0);
VerifyCopyBlocked(GL_TEXTURE_2D);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage2DMultisampleRenderbufferSampleCount1) {
if (!GLTestHelper::HasExtension("GL_CHROMIUM_framebuffer_multisample")) {
GTEST_SKIP() << "GL_CHROMIUM_framebuffer_multisample not supported";
}
SetUpMultisampleRenderbufferFBO(/*samples=*/1);
SetUpDestTexture2D();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 2, 2);
VerifyCopyBlocked(GL_TEXTURE_2D);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage3DMultisampleRenderbufferSampleCount1) {
if (!GLTestHelper::HasExtension("GL_CHROMIUM_framebuffer_multisample")) {
GTEST_SKIP() << "GL_CHROMIUM_framebuffer_multisample not supported";
}
SetUpMultisampleRenderbufferFBO(/*samples=*/1);
SetUpDestTexture3D();
glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
VerifyCopyBlocked(GL_TEXTURE_3D);
}
// Implicit Resolve Renderbuffer
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexImage2DImplicitResolveRenderbuffer) {
if (!GLTestHelper::HasExtension("GL_EXT_multisample_render_to_texture")) {
GTEST_SKIP() << "GL_EXT_multisample_render_to_texture not supported";
}
SetUpImplicitResolveRenderbufferFBO();
SetUpDestTexture2D();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 2, 2, 0);
VerifyCopyBlocked(GL_TEXTURE_2D);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage2DImplicitResolveRenderbuffer) {
if (!GLTestHelper::HasExtension("GL_EXT_multisample_render_to_texture")) {
GTEST_SKIP() << "GL_EXT_multisample_render_to_texture not supported";
}
SetUpImplicitResolveRenderbufferFBO();
SetUpDestTexture2D();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 2, 2);
VerifyCopyBlocked(GL_TEXTURE_2D);
}
TEST_F(GL3MultisampleCopyTexImageTest,
CopyTexSubImage3DImplicitResolveRenderbuffer) {
if (!GLTestHelper::HasExtension("GL_EXT_multisample_render_to_texture")) {
GTEST_SKIP() << "GL_EXT_multisample_render_to_texture not supported";
}
SetUpImplicitResolveRenderbufferFBO();
SetUpDestTexture3D();
glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
VerifyCopyBlocked(GL_TEXTURE_3D);
}
#endif // BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER)
} // namespace gpu