summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreveman <reveman@chromium.org>2015-10-06 17:10:50 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-07 00:11:52 +0000
commitf9d17986fef45ad30ef4e6e468a7a56a90851508 (patch)
treecb80ece559bcf2cb240d042fe952263a914bce3f
parent9ff4d28f4578ad22da043aebc9d73b7369eaf57b (diff)
downloadchromium_src-f9d17986fef45ad30ef4e6e468a7a56a90851508.zip
chromium_src-f9d17986fef45ad30ef4e6e468a7a56a90851508.tar.gz
chromium_src-f9d17986fef45ad30ef4e6e468a7a56a90851508.tar.bz2
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@{#352720}
-rw-r--r--build/sanitizers/lsan_suppressions.cc3
-rw-r--r--cc/test/test_image_factory.cc2
-rw-r--r--components/mus/BUILD.gn1
-rw-r--r--components/mus/gles2/command_buffer_driver.cc13
-rw-r--r--components/mus/mus_app.cc1
-rw-r--r--components/mus/vm/BUILD.gn1
-rw-r--r--content/common/gpu/gpu_channel.cc2
-rw-r--r--content/common/gpu/gpu_memory_buffer_factory_io_surface.cc4
-rw-r--r--content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc2
-rw-r--r--content/common/gpu/media/vt_video_decode_accelerator.cc7
-rw-r--r--gpu/command_buffer/service/in_process_command_buffer.cc2
-rw-r--r--ui/gl/BUILD.gn11
-rw-r--r--ui/gl/gl.gyp6
-rw-r--r--ui/gl/gl_api_unittest.cc2
-rw-r--r--ui/gl/gl_context.cc5
-rw-r--r--ui/gl/gl_gl_api_implementation.cc2
-rw-r--r--ui/gl/gl_image_io_surface.h10
-rw-r--r--ui/gl/gl_image_io_surface.mm8
-rw-r--r--ui/gl/gl_image_ref_counted_memory_unittest.cc41
-rw-r--r--ui/gl/gl_image_shared_memory.cc12
-rw-r--r--ui/gl/gl_image_shared_memory.h9
-rw-r--r--ui/gl/gl_image_shared_memory_unittest.cc43
-rw-r--r--ui/gl/gl_surface_glx.cc4
-rw-r--r--ui/gl/gl_tests.gyp4
-rw-r--r--ui/gl/test/gl_image_test_support.cc102
-rw-r--r--ui/gl/test/gl_image_test_support.h38
-rw-r--r--ui/gl/test/gl_image_test_template.h182
-rw-r--r--ui/gl/test/gl_surface_test_support.cc15
-rw-r--r--ui/gl/test/gl_surface_test_support.h4
-rw-r--r--ui/gl/test/gl_test_helper.cc141
-rw-r--r--ui/gl/test/gl_test_helper.h49
31 files changed, 675 insertions, 51 deletions
diff --git a/build/sanitizers/lsan_suppressions.cc b/build/sanitizers/lsan_suppressions.cc
index dd94d42..d1c9d22 100644
--- a/build/sanitizers/lsan_suppressions.cc
+++ b/build/sanitizers/lsan_suppressions.cc
@@ -51,6 +51,9 @@ char kLSanDefaultSuppressions[] =
// http://crbug.com/431213, http://crbug.com/416665
"leak:gin/object_template_builder.h\n"
+// Leaks in swrast_dri.so. http://crbug.com/540042
+"leak:swrast_dri.so\n"
+
// ================ Leaks in Chromium code ================
// PLEASE DO NOT ADD SUPPRESSIONS FOR NEW LEAKS.
// Instead, commits that introduce memory leaks should be reverted. Suppressing
diff --git a/cc/test/test_image_factory.cc b/cc/test/test_image_factory.cc
index 3b2415c..aaf61ac 100644
--- a/cc/test/test_image_factory.cc
+++ b/cc/test/test_image_factory.cc
@@ -24,7 +24,7 @@ scoped_refptr<gfx::GLImage> TestImageFactory::CreateImageForGpuMemoryBuffer(
scoped_refptr<gfx::GLImageSharedMemory> image(
new gfx::GLImageSharedMemory(size, internalformat));
- if (!image->Initialize(handle, format))
+ if (!image->Initialize(handle.handle, handle.id, format))
return nullptr;
return image;
diff --git a/components/mus/BUILD.gn b/components/mus/BUILD.gn
index 098bd30..ec34ba8 100644
--- a/components/mus/BUILD.gn
+++ b/components/mus/BUILD.gn
@@ -58,7 +58,6 @@ source_set("lib") {
"//mojo/common:tracing_impl",
"//ui/events",
"//ui/gl:gl",
- "//ui/gl:test_support",
"//ui/platform_window:platform_impls",
"//ui/platform_window:platform_window",
]
diff --git a/components/mus/gles2/command_buffer_driver.cc b/components/mus/gles2/command_buffer_driver.cc
index a03a357..31ebae9 100644
--- a/components/mus/gles2/command_buffer_driver.cc
+++ b/components/mus/gles2/command_buffer_driver.cc
@@ -230,11 +230,6 @@ void CommandBufferDriver::CreateImage(int32_t id,
return;
}
- gfx::GpuMemoryBufferHandle gfx_handle;
- // TODO(jam): create mojo enum for this and converter
- gfx_handle.type = static_cast<gfx::GpuMemoryBufferType>(type);
- gfx_handle.id = gfx::GpuMemoryBufferId(id);
-
MojoPlatformHandle platform_handle;
MojoResult extract_result = MojoExtractPlatformHandle(
memory_handle.release().value(), &platform_handle);
@@ -243,17 +238,17 @@ void CommandBufferDriver::CreateImage(int32_t id,
return;
}
+ base::SharedMemoryHandle handle;
#if defined(OS_WIN)
- gfx_handle.handle =
- base::SharedMemoryHandle(platform_handle, base::GetCurrentProcId());
+ handle = base::SharedMemoryHandle(platform_handle, base::GetCurrentProcId());
#else
- gfx_handle.handle = base::FileDescriptor(platform_handle, false);
+ handle = base::FileDescriptor(platform_handle, false);
#endif
scoped_refptr<gfx::GLImageSharedMemory> image =
new gfx::GLImageSharedMemory(gfx_size, internal_format);
// TODO(jam): also need a mojo enum for this enum
- if (!image->Initialize(gfx_handle, gpu_format)) {
+ if (!image->Initialize(handle, gfx::GpuMemoryBufferId(id), gpu_format)) {
NOTREACHED();
return;
}
diff --git a/components/mus/mus_app.cc b/components/mus/mus_app.cc
index 4c30240..120fc79 100644
--- a/components/mus/mus_app.cc
+++ b/components/mus/mus_app.cc
@@ -21,7 +21,6 @@
#include "ui/events/event_switches.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gl/gl_surface.h"
-#include "ui/gl/test/gl_surface_test_support.h"
#if defined(USE_X11)
#include <X11/Xlib.h>
diff --git a/components/mus/vm/BUILD.gn b/components/mus/vm/BUILD.gn
index d660c84..4bb7241 100644
--- a/components/mus/vm/BUILD.gn
+++ b/components/mus/vm/BUILD.gn
@@ -68,7 +68,6 @@ source_set("lib") {
"//ui/gfx",
"//ui/gfx/geometry",
"//ui/gl:gl",
- "//ui/gl:test_support",
"//ui/mojo/events:interfaces",
"//ui/mojo/geometry:interfaces",
"//ui/platform_window:platform_impls",
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc
index 4a43a75..c24f6a0 100644
--- a/content/common/gpu/gpu_channel.cc
+++ b/content/common/gpu/gpu_channel.cc
@@ -1037,7 +1037,7 @@ scoped_refptr<gfx::GLImage> GpuChannel::CreateImageForGpuMemoryBuffer(
case gfx::SHARED_MEMORY_BUFFER: {
scoped_refptr<gfx::GLImageSharedMemory> image(
new gfx::GLImageSharedMemory(size, internalformat));
- if (!image->Initialize(handle, format))
+ if (!image->Initialize(handle.handle, handle.id, format))
return scoped_refptr<gfx::GLImage>();
return image;
diff --git a/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc b/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc
index a6ce59f..ebe6911 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc
+++ b/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc
@@ -215,8 +215,8 @@ GpuMemoryBufferFactoryIOSurface::CreateImageForGpuMemoryBuffer(
return scoped_refptr<gfx::GLImage>();
scoped_refptr<gfx::GLImageIOSurface> image(
- new gfx::GLImageIOSurface(handle.id, size, internalformat));
- if (!image->Initialize(it->second.get(), format))
+ new gfx::GLImageIOSurface(size, internalformat));
+ if (!image->Initialize(it->second.get(), handle.id, format))
return scoped_refptr<gfx::GLImage>();
return image;
diff --git a/content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc b/content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc
index 690b8047..a73a271 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc
+++ b/content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc
@@ -105,7 +105,7 @@ GpuMemoryBufferFactorySharedMemory::CreateImageForGpuMemoryBuffer(
#endif
scoped_refptr<gfx::GLImageSharedMemory> image(
new gfx::GLImageSharedMemory(size, internalformat));
- if (!image->Initialize(handle, format))
+ if (!image->Initialize(handle.handle, handle.id, format))
return scoped_refptr<gfx::GLImage>();
return image;
diff --git a/content/common/gpu/media/vt_video_decode_accelerator.cc b/content/common/gpu/media/vt_video_decode_accelerator.cc
index c98853d..2e0ace5 100644
--- a/content/common/gpu/media/vt_video_decode_accelerator.cc
+++ b/content/common/gpu/media/vt_video_decode_accelerator.cc
@@ -1041,9 +1041,10 @@ bool VTVideoDecodeAccelerator::SendFrame(const Frame& frame) {
}
bool allow_overlay = false;
- scoped_refptr<gfx::GLImageIOSurface> gl_image(new gfx::GLImageIOSurface(
- gfx::GenericSharedMemoryId(), frame.coded_size, GL_BGRA_EXT));
- if (gl_image->Initialize(surface, gfx::BufferFormat::BGRA_8888)) {
+ scoped_refptr<gfx::GLImageIOSurface> gl_image(
+ new gfx::GLImageIOSurface(frame.coded_size, GL_BGRA_EXT));
+ if (gl_image->Initialize(surface, gfx::GenericSharedMemoryId(),
+ gfx::BufferFormat::BGRA_8888)) {
allow_overlay = true;
} else {
gl_image = nullptr;
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc
index 19e3616..59bb852 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.cc
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -699,7 +699,7 @@ void InProcessCommandBuffer::CreateImageOnGpuThread(
case gfx::SHARED_MEMORY_BUFFER: {
scoped_refptr<gfx::GLImageSharedMemory> image(
new gfx::GLImageSharedMemory(size, internalformat));
- if (!image->Initialize(handle, format)) {
+ if (!image->Initialize(handle.handle, handle.id, format)) {
LOG(ERROR) << "Failed to initialize image.";
return;
}
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index c63b290..827d105 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -285,14 +285,21 @@ source_set("gl_unittest_utils") {
}
source_set("test_support") {
+ testonly = true
sources = [
+ "test/gl_image_test_support.cc",
+ "test/gl_image_test_support.h",
+ "test/gl_image_test_template.h",
"test/gl_surface_test_support.cc",
"test/gl_surface_test_support.h",
+ "test/gl_test_helper.cc",
+ "test/gl_test_helper.h",
]
configs += [ "//third_party/khronos:khronos_headers" ]
deps = [
+ "//testing/gtest",
":gl",
]
@@ -317,6 +324,8 @@ group("gl_unittests_run") {
test("gl_unittests") {
sources = [
"gl_api_unittest.cc",
+ "gl_image_ref_counted_memory_unittest.cc",
+ "gl_image_shared_memory_unittest.cc",
"gpu_timing_unittest.cc",
"test/run_all_unittests.cc",
]
@@ -340,8 +349,10 @@ test("gl_unittests") {
":test_support",
"//base",
"//base/test:test_support",
+ "//skia",
"//testing/gmock",
"//testing/gtest",
+ "//ui/gfx",
]
}
diff --git a/ui/gl/gl.gyp b/ui/gl/gl.gyp
index f0f026a..16e8cc1 100644
--- a/ui/gl/gl.gyp
+++ b/ui/gl/gl.gyp
@@ -298,6 +298,7 @@
'target_name': 'gl_test_support',
'type': 'static_library',
'dependencies': [
+ '<(DEPTH)/testing/gtest.gyp:gtest',
'../../third_party/khronos/khronos.gyp:khronos_headers',
'gl',
],
@@ -305,8 +306,13 @@
'../..',
],
'sources': [
+ 'test/gl_image_test_support.cc',
+ 'test/gl_image_test_support.h',
+ 'test/gl_image_test_template.h',
'test/gl_surface_test_support.cc',
'test/gl_surface_test_support.h',
+ 'test/gl_test_helper.cc',
+ 'test/gl_test_helper.h',
],
'conditions': [
['use_x11==1', {
diff --git a/ui/gl/gl_api_unittest.cc b/ui/gl/gl_api_unittest.cc
index 66c9380..a5d1a9c 100644
--- a/ui/gl/gl_api_unittest.cc
+++ b/ui/gl/gl_api_unittest.cc
@@ -40,6 +40,7 @@ class GLApiTest : public testing::Test {
num_fake_extension_strings_ = 0;
fake_extension_strings_ = nullptr;
+ DCHECK(!g_current_gl_context_tls);
g_current_gl_context_tls = new base::ThreadLocalPointer<GLApi>;
SetGLGetProcAddressProc(
@@ -53,6 +54,7 @@ class GLApiTest : public testing::Test {
void TearDown() override {
api_.reset(nullptr);
delete g_current_gl_context_tls;
+ g_current_gl_context_tls = nullptr;
SetGLImplementation(kGLImplementationNone);
fake_extension_string_ = "";
diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc
index ada2127..42dadd2 100644
--- a/ui/gl/gl_context.cc
+++ b/ui/gl/gl_context.cc
@@ -206,10 +206,7 @@ bool GLContext::WasAllocatedUsingRobustnessExtension() {
bool GLContext::InitializeDynamicBindings() {
DCHECK(IsCurrent(nullptr));
- static bool initialized = false;
- if (initialized)
- return initialized;
- initialized = InitializeDynamicGLBindings(GetGLImplementation(), this);
+ bool initialized = InitializeDynamicGLBindings(GetGLImplementation(), this);
if (!initialized)
LOG(ERROR) << "Could not initialize dynamic bindings.";
return initialized;
diff --git a/ui/gl/gl_gl_api_implementation.cc b/ui/gl/gl_gl_api_implementation.cc
index 9ebb3ae..b6d5598 100644
--- a/ui/gl/gl_gl_api_implementation.cc
+++ b/ui/gl/gl_gl_api_implementation.cc
@@ -334,6 +334,8 @@ const GLVersionInfo* GetGLVersionInfo() {
}
void InitializeDynamicGLBindingsGL(GLContext* context) {
+ if (g_version_info)
+ return;
g_real_gl->InitializeFilteredExtensions();
g_driver_gl.InitializeCustomDynamicBindings(context);
DCHECK(context && context->IsCurrent(NULL) && !g_version_info);
diff --git a/ui/gl/gl_image_io_surface.h b/ui/gl/gl_image_io_surface.h
index bf65b0f..56b4478 100644
--- a/ui/gl/gl_image_io_surface.h
+++ b/ui/gl/gl_image_io_surface.h
@@ -22,11 +22,11 @@ namespace gfx {
class GL_EXPORT GLImageIOSurface : public GLImage {
public:
- GLImageIOSurface(gfx::GenericSharedMemoryId io_surface_id,
- const gfx::Size& size,
- unsigned internalformat);
+ GLImageIOSurface(const gfx::Size& size, unsigned internalformat);
- bool Initialize(IOSurfaceRef io_surface, BufferFormat format);
+ bool Initialize(IOSurfaceRef io_surface,
+ gfx::GenericSharedMemoryId io_surface_id,
+ BufferFormat format);
// Overridden from GLImage:
void Destroy(bool have_context) override;
@@ -59,11 +59,11 @@ class GL_EXPORT GLImageIOSurface : public GLImage {
~GLImageIOSurface() override;
private:
- gfx::GenericSharedMemoryId io_surface_id_;
const gfx::Size size_;
const unsigned internalformat_;
BufferFormat format_;
base::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
+ gfx::GenericSharedMemoryId io_surface_id_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(GLImageIOSurface);
diff --git a/ui/gl/gl_image_io_surface.mm b/ui/gl/gl_image_io_surface.mm
index 211226a..3f4964e 100644
--- a/ui/gl/gl_image_io_surface.mm
+++ b/ui/gl/gl_image_io_surface.mm
@@ -138,11 +138,9 @@ GLenum DataType(BufferFormat format) {
} // namespace
-GLImageIOSurface::GLImageIOSurface(gfx::GenericSharedMemoryId io_surface_id,
- const gfx::Size& size,
+GLImageIOSurface::GLImageIOSurface(const gfx::Size& size,
unsigned internalformat)
- : io_surface_id_(io_surface_id),
- size_(size),
+ : size_(size),
internalformat_(internalformat),
format_(BufferFormat::RGBA_8888) {}
@@ -152,6 +150,7 @@ GLImageIOSurface::~GLImageIOSurface() {
}
bool GLImageIOSurface::Initialize(IOSurfaceRef io_surface,
+ gfx::GenericSharedMemoryId io_surface_id,
BufferFormat format) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!io_surface_);
@@ -168,6 +167,7 @@ bool GLImageIOSurface::Initialize(IOSurfaceRef io_surface,
format_ = format;
io_surface_.reset(io_surface, base::scoped_policy::RETAIN);
+ io_surface_id_ = io_surface_id;
return true;
}
diff --git a/ui/gl/gl_image_ref_counted_memory_unittest.cc b/ui/gl/gl_image_ref_counted_memory_unittest.cc
new file mode 100644
index 0000000..1edccf2
--- /dev/null
+++ b/ui/gl/gl_image_ref_counted_memory_unittest.cc
@@ -0,0 +1,41 @@
+// 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 <vector>
+
+#include "base/memory/ref_counted_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_image_ref_counted_memory.h"
+#include "ui/gl/test/gl_image_test_template.h"
+
+namespace gfx {
+namespace {
+
+class GLImageRefCountedMemoryTestDelegate {
+ public:
+ scoped_refptr<GLImage> CreateSolidColorImage(const Size& size,
+ unsigned internalformat,
+ BufferFormat format,
+ const uint8_t color[4]) const {
+ DCHECK_EQ(NumberOfPlanesForBufferFormat(format), 1u);
+ std::vector<uint8_t> data(BufferSizeForBufferFormat(size, format));
+ scoped_refptr<base::RefCountedBytes> bytes(new base::RefCountedBytes(data));
+ GLImageTestSupport::SetBufferDataToColor(
+ size.width(), size.height(),
+ static_cast<int>(RowSizeForBufferFormat(size.width(), format, 0)),
+ format, color, &bytes->data().front());
+ scoped_refptr<GLImageRefCountedMemory> image(
+ new GLImageRefCountedMemory(size, internalformat));
+ bool rv = image->Initialize(bytes.get(), format);
+ EXPECT_TRUE(rv);
+ return image;
+ }
+};
+
+INSTANTIATE_TYPED_TEST_CASE_P(GLImageRefCountedMemory,
+ GLImageTest,
+ GLImageRefCountedMemoryTestDelegate);
+
+} // namespace
+} // namespace gfx
diff --git a/ui/gl/gl_image_shared_memory.cc b/ui/gl/gl_image_shared_memory.cc
index 33ba4d1..16ec6cb 100644
--- a/ui/gl/gl_image_shared_memory.cc
+++ b/ui/gl/gl_image_shared_memory.cc
@@ -40,16 +40,18 @@ GLImageSharedMemory::~GLImageSharedMemory() {
DCHECK(!shared_memory_);
}
-bool GLImageSharedMemory::Initialize(const gfx::GpuMemoryBufferHandle& handle,
- gfx::BufferFormat format) {
+bool GLImageSharedMemory::Initialize(
+ const base::SharedMemoryHandle& handle,
+ gfx::GenericSharedMemoryId shared_memory_id,
+ gfx::BufferFormat format) {
size_t size_in_bytes;
if (!SizeInBytes(GetSize(), format, &size_in_bytes))
return false;
- if (!base::SharedMemory::IsHandleValid(handle.handle))
+ if (!base::SharedMemory::IsHandleValid(handle))
return false;
- base::SharedMemory shared_memory(handle.handle, true);
+ base::SharedMemory shared_memory(handle, true);
// Duplicate the handle.
base::SharedMemoryHandle duped_shared_memory_handle;
@@ -73,7 +75,7 @@ bool GLImageSharedMemory::Initialize(const gfx::GpuMemoryBufferHandle& handle,
DCHECK(!shared_memory_);
shared_memory_ = duped_shared_memory.Pass();
- shared_memory_id_ = handle.id;
+ shared_memory_id_ = shared_memory_id;
return true;
}
diff --git a/ui/gl/gl_image_shared_memory.h b/ui/gl/gl_image_shared_memory.h
index d9233cb..622eb3d 100644
--- a/ui/gl/gl_image_shared_memory.h
+++ b/ui/gl/gl_image_shared_memory.h
@@ -6,7 +6,7 @@
#define UI_GL_GL_IMAGE_SHARED_MEMORY_H_
#include "base/memory/scoped_ptr.h"
-#include "ui/gfx/gpu_memory_buffer.h"
+#include "base/memory/shared_memory_handle.h"
#include "ui/gl/gl_image_memory.h"
namespace gfx {
@@ -15,8 +15,9 @@ class GL_EXPORT GLImageSharedMemory : public GLImageMemory {
public:
GLImageSharedMemory(const gfx::Size& size, unsigned internalformat);
- bool Initialize(const gfx::GpuMemoryBufferHandle& handle,
- gfx::BufferFormat format);
+ bool Initialize(const base::SharedMemoryHandle& handle,
+ gfx::GenericSharedMemoryId shared_memory_id,
+ BufferFormat format);
// Overridden from GLImage:
void Destroy(bool have_context) override;
@@ -29,7 +30,7 @@ class GL_EXPORT GLImageSharedMemory : public GLImageMemory {
private:
scoped_ptr<base::SharedMemory> shared_memory_;
- gfx::GenericSharedMemoryId shared_memory_id_;
+ GenericSharedMemoryId shared_memory_id_;
DISALLOW_COPY_AND_ASSIGN(GLImageSharedMemory);
};
diff --git a/ui/gl/gl_image_shared_memory_unittest.cc b/ui/gl/gl_image_shared_memory_unittest.cc
new file mode 100644
index 0000000..44c8e97
--- /dev/null
+++ b/ui/gl/gl_image_shared_memory_unittest.cc
@@ -0,0 +1,43 @@
+// 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 "base/memory/shared_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_image_shared_memory.h"
+#include "ui/gl/test/gl_image_test_template.h"
+
+namespace gfx {
+namespace {
+
+class GLImageSharedMemoryTestDelegate {
+ public:
+ scoped_refptr<GLImage> CreateSolidColorImage(const Size& size,
+ unsigned internalformat,
+ BufferFormat format,
+ const uint8_t color[4]) const {
+ DCHECK_EQ(NumberOfPlanesForBufferFormat(format), 1u);
+ base::SharedMemory shared_memory;
+ bool rv = shared_memory.CreateAndMapAnonymous(
+ BufferSizeForBufferFormat(size, format));
+ DCHECK(rv);
+ GLImageTestSupport::SetBufferDataToColor(
+ size.width(), size.height(),
+ static_cast<int>(RowSizeForBufferFormat(size.width(), format, 0)),
+ format, color, reinterpret_cast<uint8_t*>(shared_memory.memory()));
+ scoped_refptr<GLImageSharedMemory> image(
+ new GLImageSharedMemory(size, internalformat));
+ rv = image->Initialize(
+ base::SharedMemory::DuplicateHandle(shared_memory.handle()),
+ GenericSharedMemoryId(0), format);
+ EXPECT_TRUE(rv);
+ return image;
+ }
+};
+
+INSTANTIATE_TYPED_TEST_CASE_P(GLImageSharedMemory,
+ GLImageTest,
+ GLImageSharedMemoryTestDelegate);
+
+} // namespace
+} // namespace gfx
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index 49ff041..3f82e27 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -34,7 +34,6 @@ namespace gfx {
namespace {
Display* g_display = nullptr;
-const char* g_glx_extensions = nullptr;
bool g_glx_context_create = false;
bool g_glx_create_context_robustness_supported = false;
bool g_glx_texture_from_pixmap_supported = false;
@@ -349,7 +348,6 @@ bool GLSurfaceGLX::InitializeOneOff() {
return false;
}
- g_glx_extensions = glXQueryExtensionsString(g_display, 0);
g_glx_context_create =
HasGLXExtension("GLX_ARB_create_context");
g_glx_create_context_robustness_supported =
@@ -371,7 +369,7 @@ bool GLSurfaceGLX::InitializeOneOff() {
// static
const char* GLSurfaceGLX::GetGLXExtensions() {
- return g_glx_extensions;
+ return glXQueryExtensionsString(g_display, 0);
}
// static
diff --git a/ui/gl/gl_tests.gyp b/ui/gl/gl_tests.gyp
index 4f7e2dd..efce4a3 100644
--- a/ui/gl/gl_tests.gyp
+++ b/ui/gl/gl_tests.gyp
@@ -14,6 +14,8 @@
'test/run_all_unittests.cc',
'gpu_timing_unittest.cc',
'gl_api_unittest.cc',
+ 'gl_image_ref_counted_memory_unittest.cc',
+ 'gl_image_shared_memory_unittest.cc',
],
'include_dirs': [
'<(DEPTH)/third_party/khronos',
@@ -21,8 +23,10 @@
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
'<(DEPTH)/base/base.gyp:test_support_base',
+ '<(DEPTH)/skia/skia.gyp:skia',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/ui/gfx/gfx.gyp:gfx',
'<(DEPTH)/ui/gl/gl.gyp:gl',
'<(DEPTH)/ui/gl/gl.gyp:gl_test_support',
'<(DEPTH)/ui/gl/gl.gyp:gl_unittest_utils',
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_