| // Copyright 2014 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef GL_GLEXT_PROTOTYPES | |
| #define GL_GLEXT_PROTOTYPES | |
| #endif | |
| #include <GLES2/gl2.h> | |
| #include <GLES2/gl2ext.h> | |
| #include <GLES2/gl2extchromium.h> | |
| #include <GLES3/gl3.h> | |
| #include <stdint.h> | |
| #include <vector> | |
| #include "gpu/command_buffer/tests/gl_manager.h" | |
| #include "gpu/command_buffer/tests/gl_test_utils.h" | |
| #include "gpu/config/gpu_test_config.h" | |
| #include "testing/gmock/include/gmock/gmock.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| #include "ui/gfx/extension_set.h" | |
| #include "ui/gl/gl_context.h" | |
| #include "ui/gl/gl_version_info.h" | |
| namespace gpu { | |
| // A collection of tests that exercise the glClear workaround. | |
| class GLClearFramebufferTest : public testing::TestWithParam<bool> { | |
| public: | |
| GLClearFramebufferTest() : color_handle_(0u), depth_handle_(0u) {} | |
| protected: | |
| void SetUp() override { | |
| if (GetParam()) { | |
| // Force the glClear() workaround so we can test it here. | |
| GpuDriverBugWorkarounds workarounds; | |
| workarounds.gl_clear_broken = true; | |
| gl_.InitializeWithWorkarounds(GLManager::Options(), workarounds); | |
| DCHECK(gl_.workarounds().gl_clear_broken); | |
| } else { | |
| gl_.Initialize(GLManager::Options()); | |
| DCHECK(!gl_.workarounds().gl_clear_broken); | |
| } | |
| } | |
| bool IsApplicable() { | |
| // The workaround doesn't use VAOs which would cause a failure on a core | |
| // context and the hardware for each the workaround is necessary has a buggy | |
| // VAO implementation. So we skip testing the workaround on core profiles. | |
| return !GetParam() || | |
| !gl_.context()->GetVersionInfo()->is_desktop_core_profile; | |
| } | |
| void InitDraw(); | |
| void SetDrawColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); | |
| void SetDrawDepth(GLfloat depth); | |
| void DrawQuad(); | |
| void TearDown() override { | |
| GLTestHelper::CheckGLError("no errors", __LINE__); | |
| gl_.Destroy(); | |
| } | |
| private: | |
| GLManager gl_; | |
| GLuint color_handle_; | |
| GLuint depth_handle_; | |
| }; | |
| void GLClearFramebufferTest::InitDraw() { | |
| static const char* v_shader_str = | |
| "attribute vec4 a_Position;\n" | |
| "uniform float u_depth;\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_Position = a_Position;\n" | |
| " gl_Position.z = u_depth;\n" | |
| "}\n"; | |
| static const char* f_shader_str = | |
| "precision mediump float;\n" | |
| "uniform vec4 u_draw_color;\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_FragColor = u_draw_color;\n" | |
| "}\n"; | |
| GLuint program = GLTestHelper::LoadProgram(v_shader_str, f_shader_str); | |
| DCHECK(program); | |
| glUseProgram(program); | |
| GLuint position_loc = glGetAttribLocation(program, "a_Position"); | |
| GLTestHelper::SetupUnitQuad(position_loc); | |
| color_handle_ = glGetUniformLocation(program, "u_draw_color"); | |
| DCHECK(color_handle_ != static_cast<GLuint>(-1)); | |
| depth_handle_ = glGetUniformLocation(program, "u_depth"); | |
| DCHECK(depth_handle_ != static_cast<GLuint>(-1)); | |
| } | |
| void GLClearFramebufferTest::SetDrawColor(GLfloat r, | |
| GLfloat g, | |
| GLfloat b, | |
| GLfloat a) { | |
| glUniform4f(color_handle_, r, g, b, a); | |
| } | |
| void GLClearFramebufferTest::SetDrawDepth(GLfloat depth) { | |
| glUniform1f(depth_handle_, depth); | |
| } | |
| void GLClearFramebufferTest::DrawQuad() { | |
| glDrawArrays(GL_TRIANGLES, 0, 6); | |
| } | |
| INSTANTIATE_TEST_CASE_P(GLClearFramebufferTestWithParam, | |
| GLClearFramebufferTest, | |
| ::testing::Values(true, false)); | |
| TEST_P(GLClearFramebufferTest, ClearColor) { | |
| if (!IsApplicable()) { | |
| return; | |
| } | |
| glClearColor(1.0f, 0.5f, 0.25f, 0.5f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| // Verify. | |
| const uint8_t expected[] = {255, 128, 64, 128}; | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 1 /* tolerance */, expected, | |
| nullptr)); | |
| } | |
| TEST_P(GLClearFramebufferTest, ClearColorWithMask) { | |
| if (!IsApplicable()) { | |
| return; | |
| } | |
| glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE); | |
| glClearColor(1.0f, 1.0f, 1.0f, 1.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| // Verify. | |
| const uint8_t expected[] = {255, 0, 0, 0}; | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, expected, | |
| nullptr)); | |
| } | |
| // crbug.com/434094 | |
| #if !defined(OS_MACOSX) | |
| TEST_P(GLClearFramebufferTest, ClearColorWithScissor) { | |
| if (!IsApplicable()) { | |
| return; | |
| } | |
| glClearColor(1.0f, 1.0f, 1.0f, 1.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| // Verify. | |
| const uint8_t expected[] = {255, 255, 255, 255}; | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, expected, | |
| nullptr)); | |
| glScissor(0, 0, 0, 0); | |
| glEnable(GL_SCISSOR_TEST); | |
| glClearColor(0, 0, 0, 0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| // Verify - no changes. | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, expected, | |
| nullptr)); | |
| } | |
| #endif | |
| TEST_P(GLClearFramebufferTest, ClearDepthStencil) { | |
| if (!IsApplicable()) { | |
| return; | |
| } | |
| // TODO(kainino): https://crbug.com/782317 | |
| if (GPUTestBotConfig::CurrentConfigMatches("Intel")) { | |
| return; | |
| } | |
| const GLuint kStencilRef = 1 << 2; | |
| InitDraw(); | |
| SetDrawColor(1.0f, 0.0f, 0.0f, 1.0f); | |
| DrawQuad(); | |
| // Verify. | |
| const uint8_t kRed[] = {255, 0, 0, 255}; | |
| const uint8_t kGreen[] = {0, 255, 0, 255}; | |
| EXPECT_TRUE( | |
| GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, kRed, nullptr)); | |
| glClearStencil(kStencilRef); | |
| glClear(GL_STENCIL_BUFFER_BIT); | |
| glEnable(GL_STENCIL_TEST); | |
| glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); | |
| glStencilFunc(GL_NOTEQUAL, kStencilRef, 0xFFFFFFFF); | |
| SetDrawColor(0.0f, 1.0f, 0.0f, 1.0f); | |
| DrawQuad(); | |
| // Verify - stencil should have failed, so still red. | |
| EXPECT_TRUE( | |
| GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, kRed, nullptr)); | |
| glStencilFunc(GL_EQUAL, kStencilRef, 0xFFFFFFFF); | |
| DrawQuad(); | |
| // Verify - stencil should have passed, so green. | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, kGreen, | |
| nullptr)); | |
| glEnable(GL_DEPTH_TEST); | |
| glClearDepthf(0.0f); | |
| glClear(GL_DEPTH_BUFFER_BIT); | |
| SetDrawDepth(0.5f); | |
| SetDrawColor(1.0f, 0.0f, 0.0f, 1.0f); | |
| DrawQuad(); | |
| // Verify - depth test should have failed, so still green. | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, kGreen, | |
| nullptr)); | |
| glClearDepthf(0.9f); | |
| glClear(GL_DEPTH_BUFFER_BIT); | |
| DrawQuad(); | |
| // Verify - depth test should have passed, so red. | |
| EXPECT_TRUE( | |
| GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, kRed, nullptr)); | |
| } | |
| TEST_P(GLClearFramebufferTest, SeparateFramebufferClear) { | |
| const char* extension_string = | |
| reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); | |
| gfx::ExtensionSet extensions = gfx::MakeExtensionSet(extension_string); | |
| bool has_separate_framebuffer = | |
| gfx::HasExtension(extensions, "GL_CHROMIUM_framebuffer_multisample"); | |
| if (!IsApplicable() || !has_separate_framebuffer) { | |
| return; | |
| } | |
| glClearColor(0.f, 0.f, 0.f, 1.f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| // Bind incomplete read framebuffer, should not affect clear. | |
| GLuint fbo; | |
| glGenFramebuffers(1, &fbo); | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); | |
| EXPECT_NE(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER), | |
| static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE)); | |
| glClearColor(1.f, 0.f, 0.f, 1.f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | |
| const uint8_t kRed[] = {255, 0, 0, 255}; | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0, kRed, nullptr)); | |
| // Bind complete, but smaller read framebuffer, should not affect clear. | |
| GLuint texture; | |
| glGenTextures(1, &texture); | |
| glBindTexture(GL_TEXTURE_2D, texture); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| nullptr); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| GL_TEXTURE_2D, texture, 0); | |
| EXPECT_EQ(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER), | |
| static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE)); | |
| glClearColor(0.f, 1.f, 0.f, 1.f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | |
| const uint8_t kGreen[] = {0, 255, 0, 255}; | |
| EXPECT_TRUE(GLTestHelper::CheckPixels(3, 3, 1, 1, 0, kGreen, nullptr)); | |
| } | |
| class ES3ClearBufferTest : public testing::Test { | |
| protected: | |
| static const GLsizei kCanvasSize = 4; | |
| void SetUp() override { | |
| GLManager::Options options; | |
| options.size = gfx::Size(kCanvasSize, kCanvasSize); | |
| options.context_type = CONTEXT_TYPE_OPENGLES3; | |
| gl_.Initialize(options); | |
| } | |
| bool ShouldSkipTest() const { | |
| // If a driver isn't capable of supporting ES3 context, creating | |
| // ContextGroup will fail. | |
| // See crbug.com/654709. | |
| return (!gl_.decoder() || !gl_.decoder()->GetContextGroup()); | |
| } | |
| void TearDown() override { | |
| GLTestHelper::CheckGLError("no errors", __LINE__); | |
| gl_.Destroy(); | |
| } | |
| GLManager gl_; | |
| }; | |
| TEST_F(ES3ClearBufferTest, ClearBuffersuiv) { | |
| // This is a regression test for https://crbug.com/908749 | |
| GLuint value[1] = {0u}; | |
| glClearBufferuiv(GL_STENCIL, 0, value); | |
| // The above call should not crash in ASAN build. | |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), glGetError()); | |
| } | |
| } // namespace gpu |