diff options
author | reveman <reveman@chromium.org> | 2015-10-06 21:12:45 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-07 04:13:43 +0000 |
commit | 11b3655a4dfd5b46f7783421e2959a41ddbf3a00 (patch) | |
tree | fa883b38c1defaf24c1407fc98fd28a38f64d1de /ui/gl/test | |
parent | a15b1645432b1ee5d9c9ade87da3a76b51b01a67 (diff) | |
download | chromium_src-11b3655a4dfd5b46f7783421e2959a41ddbf3a00.zip chromium_src-11b3655a4dfd5b46f7783421e2959a41ddbf3a00.tar.gz chromium_src-11b3655a4dfd5b46f7783421e2959a41ddbf3a00.tar.bz2 |
Re-land: ui: Add GLImage unit test framework.
This makes it possible to test GLImage implementations without
requiring multi-process GpuMemoryBuffer support.
This initial version is limited to testing CopyTexSubImage with
the default buffer format but testing of more functionality and
formats will be added in follow up patches.
Also inlcudes some minor cleanup needed to not have GLImage
implementations depend on GpuMemoryBuffer API.
BUG=
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel
Review URL: https://codereview.chromium.org/1354483004
Cr-Commit-Position: refs/heads/master@{#352767}
Diffstat (limited to 'ui/gl/test')
-rw-r--r-- | ui/gl/test/gl_image_test_support.cc | 102 | ||||
-rw-r--r-- | ui/gl/test/gl_image_test_support.h | 38 | ||||
-rw-r--r-- | ui/gl/test/gl_image_test_template.h | 182 | ||||
-rw-r--r-- | ui/gl/test/gl_surface_test_support.cc | 15 | ||||
-rw-r--r-- | ui/gl/test/gl_surface_test_support.h | 4 | ||||
-rw-r--r-- | ui/gl/test/gl_test_helper.cc | 141 | ||||
-rw-r--r-- | ui/gl/test/gl_test_helper.h | 49 |
7 files changed, 526 insertions, 5 deletions
diff --git a/ui/gl/test/gl_image_test_support.cc b/ui/gl/test/gl_image_test_support.cc new file mode 100644 index 0000000..c6f5bf3 --- /dev/null +++ b/ui/gl/test/gl_image_test_support.cc @@ -0,0 +1,102 @@ +// Copyright 2015 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. + +#include "ui/gl/test/gl_image_test_support.h" + +#include <vector> + +#include "third_party/skia/include/core/SkTypes.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_version_info.h" +#include "ui/gl/test/gl_surface_test_support.h" + +#if defined(USE_OZONE) +#include "base/message_loop/message_loop.h" +#endif + +namespace gfx { + +// static +void GLImageTestSupport::InitializeGL() { +#if defined(USE_OZONE) + // On Ozone, the backend initializes the event system using a UI thread. + base::MessageLoopForUI main_loop; +#endif + + std::vector<GLImplementation> allowed_impls; + GetAllowedGLImplementations(&allowed_impls); + DCHECK(!allowed_impls.empty()); + + GLImplementation impl = allowed_impls[0]; + GLSurfaceTestSupport::InitializeOneOffImplementation(impl, true); +} + +// static +void GLImageTestSupport::CleanupGL() { + ClearGLBindings(); +} + +// static +GLenum GLImageTestSupport::GetPreferredInternalFormat() { + bool has_texture_format_bgra8888 = + GLContext::GetCurrent()->HasExtension( + "GL_APPLE_texture_format_BGRA8888") || + GLContext::GetCurrent()->HasExtension("GL_EXT_texture_format_BGRA8888") || + !GLContext::GetCurrent()->GetVersionInfo()->is_es; + return (!SK_B32_SHIFT && has_texture_format_bgra8888) ? GL_BGRA_EXT : GL_RGBA; +} + +// static +BufferFormat GLImageTestSupport::GetPreferredBufferFormat() { + return GetPreferredInternalFormat() == GL_BGRA_EXT ? BufferFormat::BGRA_8888 + : BufferFormat::RGBA_8888; +} + +// static +void GLImageTestSupport::SetBufferDataToColor(int width, + int height, + int stride, + BufferFormat format, + const uint8_t color[4], + uint8_t* data) { + switch (format) { + case BufferFormat::RGBA_8888: + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + data[y * stride + x * 4 + 0] = color[0]; + data[y * stride + x * 4 + 1] = color[1]; + data[y * stride + x * 4 + 2] = color[2]; + data[y * stride + x * 4 + 3] = color[3]; + } + } + return; + case BufferFormat::BGRA_8888: + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + data[y * stride + x * 4 + 0] = color[2]; + data[y * stride + x * 4 + 1] = color[1]; + data[y * stride + x * 4 + 2] = color[0]; + data[y * stride + x * 4 + 3] = color[3]; + } + } + return; + case BufferFormat::ATC: + case BufferFormat::ATCIA: + case BufferFormat::DXT1: + case BufferFormat::DXT5: + case BufferFormat::ETC1: + case BufferFormat::R_8: + case BufferFormat::RGBA_4444: + case BufferFormat::BGRX_8888: + case BufferFormat::UYVY_422: + case BufferFormat::YUV_420_BIPLANAR: + case BufferFormat::YUV_420: + NOTREACHED(); + return; + } + NOTREACHED(); +} + +} // namespace gfx diff --git a/ui/gl/test/gl_image_test_support.h b/ui/gl/test/gl_image_test_support.h new file mode 100644 index 0000000..782f207 --- /dev/null +++ b/ui/gl/test/gl_image_test_support.h @@ -0,0 +1,38 @@ +// Copyright 2015 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 UI_GL_TEST_GL_IMAGE_TEST_SUPPORT_H_ +#define UI_GL_TEST_GL_IMAGE_TEST_SUPPORT_H_ + +#include "ui/gfx/buffer_types.h" +#include "ui/gl/gl_bindings.h" + +namespace gfx { + +class GLImageTestSupport { + public: + // Initialize GL for image testing. + static void InitializeGL(); + + // Cleanup GL after being initialized for image testing. + static void CleanupGL(); + + // Returns the preferred internal format used for images. + static GLenum GetPreferredInternalFormat(); + + // Returns the preferred buffer format used for images. + static BufferFormat GetPreferredBufferFormat(); + + // Initialize buffer of a specific |format| to |color|. + static void SetBufferDataToColor(int width, + int height, + int stride, + BufferFormat format, + const uint8_t color[4], + uint8_t* data); +}; + +} // namespace gfx + +#endif // UI_GL_TEST_GL_IMAGE_TEST_SUPPORT_H_ diff --git a/ui/gl/test/gl_image_test_template.h b/ui/gl/test/gl_image_test_template.h new file mode 100644 index 0000000..8533ce2 --- /dev/null +++ b/ui/gl/test/gl_image_test_template.h @@ -0,0 +1,182 @@ +// Copyright 2015 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. + +// This file defines tests that implementations of GLImage should pass in order +// to be conformant. + +#ifndef UI_GL_TEST_GL_IMAGE_TEST_TEMPLATE_H_ +#define UI_GL_TEST_GL_IMAGE_TEST_TEMPLATE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/stringize_macros.h" +#include "base/strings/stringprintf.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/buffer_format_util.h" +#include "ui/gfx/buffer_types.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_image.h" +#include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface.h" +#include "ui/gl/test/gl_image_test_support.h" +#include "ui/gl/test/gl_test_helper.h" + +namespace gfx { + +template <typename GLImageTestDelegate> +class GLImageTest : public testing::Test { + protected: + // Overridden from testing::Test: + void SetUp() override { + GLImageTestSupport::InitializeGL(); + surface_ = GLSurface::CreateOffscreenGLSurface(Size()); + context_ = GLContext::CreateGLContext(nullptr, surface_.get(), + PreferIntegratedGpu); + context_->MakeCurrent(surface_.get()); + } + void TearDown() override { + context_->ReleaseCurrent(surface_.get()); + context_ = nullptr; + surface_ = nullptr; + GLImageTestSupport::CleanupGL(); + } + + protected: + scoped_refptr<GLSurface> surface_; + scoped_refptr<GLContext> context_; + GLImageTestDelegate delegate_; +}; + +TYPED_TEST_CASE_P(GLImageTest); + +// Copy image to texture. Support is optional. Texels should be updated if +// supported, and left unchanged if not. +TYPED_TEST_P(GLImageTest, CopyTexSubImage) { + const Size image_size(256, 256); + const uint8_t image_color[] = {0, 0xff, 0, 0xff}; + const uint8_t texture_color[] = {0, 0, 0xff, 0xff}; + + GLuint framebuffer = + GLTestHelper::SetupFramebuffer(image_size.width(), image_size.height()); + ASSERT_TRUE(framebuffer); + glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer); + glViewport(0, 0, image_size.width(), image_size.height()); + + // Create a solid color green image of preferred format. This must succeed + // in order for a GLImage to be conformant. + scoped_refptr<GLImage> image = this->delegate_.CreateSolidColorImage( + image_size, GLImageTestSupport::GetPreferredInternalFormat(), + GLImageTestSupport::GetPreferredBufferFormat(), image_color); + ASSERT_TRUE(image); + + // Create a solid color blue texture of the same size as |image|. + GLuint texture = GLTestHelper::CreateTexture(GL_TEXTURE_2D); + scoped_ptr<uint8_t[]> pixels(new uint8_t[BufferSizeForBufferFormat( + image_size, GLImageTestSupport::GetPreferredBufferFormat())]); + GLImageTestSupport::SetBufferDataToColor( + image_size.width(), image_size.height(), + static_cast<int>(RowSizeForBufferFormat( + image_size.width(), GLImageTestSupport::GetPreferredBufferFormat(), + 0)), + GLImageTestSupport::GetPreferredBufferFormat(), texture_color, + pixels.get()); + // Note: This test assume that |image| can be used with GL_TEXTURE_2D but + // that might not be the case for some GLImage implementations. + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, + GLImageTestSupport::GetPreferredInternalFormat(), + image_size.width(), image_size.height(), 0, + GLImageTestSupport::GetPreferredInternalFormat(), + GL_UNSIGNED_BYTE, pixels.get()); + + // Attempt to copy |image| to |texture|. + // Returns true on success, false on failure. + bool rv = image->CopyTexSubImage(GL_TEXTURE_2D, Point(), Rect(image_size)); + + // clang-format off + const char kVertexShader[] = STRINGIZE( + attribute vec2 a_position; + attribute vec2 a_texCoord; + varying vec2 v_texCoord; + void main() { + gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0); + v_texCoord = a_texCoord; + } + ); + const char kFragmentShader[] = STRINGIZE( + uniform sampler2D a_texture; + varying vec2 v_texCoord; + void main() { + gl_FragColor = texture2D(a_texture, v_texCoord); + } + ); + const char kShaderFloatPrecision[] = STRINGIZE( + precision mediump float; + ); + // clang-format on + + GLuint vertex_shader = + GLTestHelper::LoadShader(GL_VERTEX_SHADER, kVertexShader); + bool is_gles = GetGLImplementation() == kGLImplementationEGLGLES2; + GLuint fragment_shader = GLTestHelper::LoadShader( + GL_FRAGMENT_SHADER, + base::StringPrintf("%s%s", is_gles ? kShaderFloatPrecision : "", + kFragmentShader) + .c_str()); + GLuint program = GLTestHelper::SetupProgram(vertex_shader, fragment_shader); + EXPECT_NE(program, 0u); + glUseProgram(program); + + GLint sampler_location = glGetUniformLocation(program, "a_texture"); + ASSERT_NE(sampler_location, -1); + glUniform1i(sampler_location, 0); + + // clang-format off + static GLfloat vertices[] = { + -1.f, -1.f, 0.f, 0.f, + 1.f, -1.f, 1.f, 0.f, + -1.f, 1.f, 0.f, 1.f, + 1.f, 1.f, 1.f, 1.f + }; + // clang-format on + + GLuint vertex_buffer; + glGenBuffersARB(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + GLint position_location = glGetAttribLocation(program, "a_position"); + ASSERT_NE(position_location, -1); + glEnableVertexAttribArray(position_location); + glVertexAttribPointer(position_location, 2, GL_FLOAT, GL_FALSE, + sizeof(GLfloat) * 4, 0); + GLint tex_coord_location = glGetAttribLocation(program, "a_texCoord"); + EXPECT_NE(tex_coord_location, -1); + glEnableVertexAttribArray(tex_coord_location); + glVertexAttribPointer(tex_coord_location, 2, GL_FLOAT, GL_FALSE, + sizeof(GLfloat) * 4, + reinterpret_cast<void*>(sizeof(GLfloat) * 2)); + + // Draw |texture| to viewport and read back pixels to check expectations. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + GLTestHelper::CheckPixels(0, 0, image_size.width(), image_size.height(), + rv ? image_color : texture_color); + + // Clean up. + glDeleteProgram(program); + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + glDeleteBuffersARB(1, &vertex_buffer); + glDeleteTextures(1, &texture); + glDeleteFramebuffersEXT(1, &framebuffer); + image->Destroy(true); +} + +// The GLImageTest test case verifies behaviour that is expected from a +// GLImage in order to be conformant. +REGISTER_TYPED_TEST_CASE_P(GLImageTest, CopyTexSubImage); + +} // namespace gfx + +#endif // UI_GL_TEST_GL_IMAGE_TEST_TEMPLATE_H_ diff --git a/ui/gl/test/gl_surface_test_support.cc b/ui/gl/test/gl_surface_test_support.cc index ecc8015..99ae718 100644 --- a/ui/gl/test/gl_surface_test_support.cc +++ b/ui/gl/test/gl_surface_test_support.cc @@ -61,21 +61,26 @@ void GLSurfaceTestSupport::InitializeOneOff() { } // static -void GLSurfaceTestSupport::InitializeOneOffWithMockBindings() { +void GLSurfaceTestSupport::InitializeOneOffImplementation( + GLImplementation impl, + bool fallback_to_osmesa) { DCHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) << "kUseGL has not effect in tests"; // This method may be called multiple times in the same process to set up - // mock bindings in different ways. + // bindings in different ways. ClearGLBindings(); - bool fallback_to_osmesa = false; bool gpu_service_logging = false; bool disable_gl_drawing = false; CHECK(GLSurface::InitializeOneOffImplementation( - kGLImplementationMockGL, fallback_to_osmesa, gpu_service_logging, - disable_gl_drawing)); + impl, fallback_to_osmesa, gpu_service_logging, disable_gl_drawing)); +} + +// static +void GLSurfaceTestSupport::InitializeOneOffWithMockBindings() { + InitializeOneOffImplementation(kGLImplementationMockGL, false); } // static diff --git a/ui/gl/test/gl_surface_test_support.h b/ui/gl/test/gl_surface_test_support.h index 6e53c69..eb70ef0 100644 --- a/ui/gl/test/gl_surface_test_support.h +++ b/ui/gl/test/gl_surface_test_support.h @@ -5,6 +5,8 @@ #ifndef UI_GL_TEST_GL_SURFACE_TEST_SUPPORT_H_ #define UI_GL_TEST_GL_SURFACE_TEST_SUPPORT_H_ +#include "ui/gl/gl_implementation.h" + namespace gfx { class GLContext; @@ -12,6 +14,8 @@ class GLContext; class GLSurfaceTestSupport { public: static void InitializeOneOff(); + static void InitializeOneOffImplementation(GLImplementation impl, + bool fallback_to_osmesa); static void InitializeOneOffWithMockBindings(); static void InitializeDynamicMockBindings(GLContext* context); }; diff --git a/ui/gl/test/gl_test_helper.cc b/ui/gl/test/gl_test_helper.cc new file mode 100644 index 0000000..052a1b9 --- /dev/null +++ b/ui/gl/test/gl_test_helper.cc @@ -0,0 +1,141 @@ +// Copyright 2015 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. + +#include "ui/gl/test/gl_test_helper.h" + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gfx { + +// static +GLuint GLTestHelper::CreateTexture(GLenum target) { + // Create the texture object. + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(target, texture); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + return texture; +} + +// static +GLuint GLTestHelper::CompileShader(GLenum type, const char* src) { + GLuint shader = glCreateShader(type); + // Load the shader source. + glShaderSource(shader, 1, &src, nullptr); + // Compile the shader. + glCompileShader(shader); + return shader; +} + +// static +GLuint GLTestHelper::LoadShader(GLenum type, const char* src) { + GLuint shader = CompileShader(type, src); + + // Check the compile status. + GLint value = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &value); + if (!value) { + char buffer[1024]; + GLsizei length = 0; + glGetShaderInfoLog(shader, sizeof(buffer), &length, buffer); + std::string log(buffer, length); + EXPECT_EQ(1, value) << "Error compiling shader: " << log; + glDeleteShader(shader); + shader = 0; + } + return shader; +} + +// static +GLuint GLTestHelper::LinkProgram(GLuint vertex_shader, GLuint fragment_shader) { + // Create the program object. + GLuint program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + // Link the program. + glLinkProgram(program); + return program; +} + +// static +GLuint GLTestHelper::SetupProgram(GLuint vertex_shader, + GLuint fragment_shader) { + GLuint program = LinkProgram(vertex_shader, fragment_shader); + // Check the link status. + GLint linked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &linked); + if (!linked) { + char buffer[1024]; + GLsizei length = 0; + glGetProgramInfoLog(program, sizeof(buffer), &length, buffer); + std::string log(buffer, length); + EXPECT_EQ(1, linked) << "Error linking program: " << log; + glDeleteProgram(program); + program = 0; + } + return program; +} + +// static +GLuint GLTestHelper::SetupFramebuffer(int width, int height) { + GLuint color_buffer_texture = CreateTexture(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, color_buffer_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + GLuint framebuffer = 0; + glGenFramebuffersEXT(1, &framebuffer); + glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + color_buffer_texture, 0); + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)) + << "Error setting up framebuffer"; + glDeleteFramebuffersEXT(1, &framebuffer); + framebuffer = 0; + } + glBindFramebufferEXT(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, &color_buffer_texture); + return framebuffer; +} + +// static +bool GLTestHelper::CheckPixels(int x, + int y, + int width, + int height, + const uint8_t expected_color[4]) { + int size = width * height * 4; + scoped_ptr<uint8_t[]> pixels(new uint8_t[size]); + const uint8_t kCheckClearValue = 123u; + memset(pixels.get(), kCheckClearValue, size); + glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get()); + int bad_count = 0; + for (int yy = 0; yy < height; ++yy) { + for (int xx = 0; xx < width; ++xx) { + int offset = yy * width * 4 + xx * 4; + for (int jj = 0; jj < 4; ++jj) { + uint8_t actual = pixels[offset + jj]; + uint8_t expected = expected_color[jj]; + EXPECT_EQ(expected, actual) << " at " << (xx + x) << ", " << (yy + y) + << " channel " << jj; + bad_count += actual != expected; + // Exit early just so we don't spam the log but we print enough to + // hopefully make it easy to diagnose the issue. + if (bad_count > 16) + return false; + } + } + } + + return !bad_count; +} + +} // namespace gfx diff --git a/ui/gl/test/gl_test_helper.h b/ui/gl/test/gl_test_helper.h new file mode 100644 index 0000000..f566525 --- /dev/null +++ b/ui/gl/test/gl_test_helper.h @@ -0,0 +1,49 @@ +// Copyright 2015 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 UI_GL_TEST_GL_TEST_HELPER_H_ +#define UI_GL_TEST_GL_TEST_HELPER_H_ + +#include "base/basictypes.h" +#include "ui/gl/gl_bindings.h" + +namespace gfx { + +class GLTestHelper { + public: + // Creates a texture object. + // Does not check for errors, always returns texture. + static GLuint CreateTexture(GLenum target); + + // Compiles a shader. + // Does not check for errors, always returns shader. + static GLuint CompileShader(GLenum type, const char* src); + + // Compiles a shader and checks for compilation errors. + // Returns shader, 0 on failure. + static GLuint LoadShader(GLenum type, const char* src); + + // Attaches 2 shaders and links them to a program. + // Does not check for errors, always returns program. + static GLuint LinkProgram(GLuint vertex_shader, GLuint fragment_shader); + + // Attaches 2 shaders, links them to a program, and checks for errors. + // Returns program, 0 on failure. + static GLuint SetupProgram(GLuint vertex_shader, GLuint fragment_shader); + + // Creates a framebuffer, attaches a color buffer, and checks for errors. + // Returns framebuffer, 0 on failure. + static GLuint SetupFramebuffer(int width, int height); + + // Checks an area of pixels for a color. + static bool CheckPixels(GLint x, + GLint y, + GLsizei width, + GLsizei height, + const uint8_t expected_color[4]); +}; + +} // namespace gfx + +#endif // UI_GL_TEST_GL_TEST_HELPER_H_ |