From f2322f7fb01ba0dc954c94f48bb5e9a9a41c32e2 Mon Sep 17 00:00:00 2001
From: reveman <reveman@chromium.org>
Date: Tue, 10 Nov 2015 10:12:52 -0800
Subject: Re-land: ui: Add custom stride support to GLImageMemory.

This improves our support for shared memory pools by allowing
clients to create GLImages with a stride that is larger than
the row length of the image.

This makes it possible to import a buffer with a stride that
doesn't match the aligned row size. However, support for
mapping of such a buffer is not provided by this patch.

BUG=549781
TEST=gl_unittests --gtest_filter=GLImageSharedMemoryPool/GLImageCopyTest/0.CopyTexImage
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

Review URL: https://codereview.chromium.org/1403283007

Cr-Commit-Position: refs/heads/master@{#358850}
---
 cc/test/test_gpu_memory_buffer_manager.cc          |  15 ++-
 cc/test/test_image_factory.cc                      |   4 +-
 components/mus/gles2/command_buffer_driver.cc      |   5 +-
 components/mus/gles2/command_buffer_local.cc       |   5 +-
 components/mus/gles2/mojo_gpu_memory_buffer.cc     |   1 +
 .../gpu/browser_gpu_memory_buffer_manager.cc       |   1 +
 content/common/child_process_messages.h            |   1 +
 content/common/gpu/client/gpu_channel_host.cc      |   1 +
 .../client/gpu_memory_buffer_impl_shared_memory.cc |  16 ++-
 .../client/gpu_memory_buffer_impl_shared_memory.h  |   4 +-
 content/common/gpu/gpu_channel.cc                  |   7 +-
 .../service/in_process_command_buffer.cc           |   9 +-
 ui/gfx/gpu_memory_buffer.h                         |   1 +
 ui/gl/gl_image_memory.cc                           | 127 +++++++++++++++++----
 ui/gl/gl_image_memory.h                            |   6 +-
 ui/gl/gl_image_ref_counted_memory.cc               |   6 +-
 ui/gl/gl_image_shared_memory.cc                    |  22 ++--
 ui/gl/gl_image_shared_memory.h                     |   3 +-
 ui/gl/gl_image_shared_memory_unittest.cc           |  18 +--
 19 files changed, 193 insertions(+), 59 deletions(-)

diff --git a/cc/test/test_gpu_memory_buffer_manager.cc b/cc/test/test_gpu_memory_buffer_manager.cc
index ed5e713..af86cbb 100644
--- a/cc/test/test_gpu_memory_buffer_manager.cc
+++ b/cc/test/test_gpu_memory_buffer_manager.cc
@@ -17,16 +17,19 @@ class GpuMemoryBufferImpl : public gfx::GpuMemoryBuffer {
   GpuMemoryBufferImpl(const gfx::Size& size,
                       gfx::BufferFormat format,
                       scoped_ptr<base::SharedMemory> shared_memory,
-                      size_t offset)
+                      size_t offset,
+                      size_t stride)
       : size_(size),
         format_(format),
         shared_memory_(shared_memory.Pass()),
         offset_(offset),
+        stride_(stride),
         mapped_(false) {}
 
   // Overridden from gfx::GpuMemoryBuffer:
   bool Map() override {
     DCHECK(!mapped_);
+    DCHECK_EQ(stride_, gfx::RowSizeForBufferFormat(size_.width(), format_, 0));
     if (!shared_memory_->Map(offset_ +
                              gfx::BufferSizeForBufferFormat(size_, format_)))
       return false;
@@ -60,6 +63,7 @@ class GpuMemoryBufferImpl : public gfx::GpuMemoryBuffer {
     handle.type = gfx::SHARED_MEMORY_BUFFER;
     handle.handle = shared_memory_->handle();
     handle.offset = base::checked_cast<uint32_t>(offset_);
+    handle.stride = base::checked_cast<int32_t>(stride_);
     return handle;
   }
   ClientBuffer AsClientBuffer() override {
@@ -71,6 +75,7 @@ class GpuMemoryBufferImpl : public gfx::GpuMemoryBuffer {
   gfx::BufferFormat format_;
   scoped_ptr<base::SharedMemory> shared_memory_;
   size_t offset_;
+  size_t stride_;
   bool mapped_;
 };
 
@@ -90,8 +95,10 @@ TestGpuMemoryBufferManager::AllocateGpuMemoryBuffer(const gfx::Size& size,
   const size_t buffer_size = gfx::BufferSizeForBufferFormat(size, format);
   if (!shared_memory->CreateAnonymous(buffer_size))
     return nullptr;
-  return make_scoped_ptr<gfx::GpuMemoryBuffer>(
-      new GpuMemoryBufferImpl(size, format, shared_memory.Pass(), 0));
+  return make_scoped_ptr<gfx::GpuMemoryBuffer>(new GpuMemoryBufferImpl(
+      size, format, shared_memory.Pass(), 0,
+      base::checked_cast<int>(
+          gfx::RowSizeForBufferFormat(size.width(), format, 0))));
 }
 
 scoped_ptr<gfx::GpuMemoryBuffer>
@@ -105,7 +112,7 @@ TestGpuMemoryBufferManager::CreateGpuMemoryBufferFromHandle(
   return make_scoped_ptr<gfx::GpuMemoryBuffer>(new GpuMemoryBufferImpl(
       size, format,
       make_scoped_ptr(new base::SharedMemory(handle.handle, false)),
-      handle.offset));
+      handle.offset, handle.stride));
 }
 
 gfx::GpuMemoryBuffer*
diff --git a/cc/test/test_image_factory.cc b/cc/test/test_image_factory.cc
index 69070e7..79c2e7e 100644
--- a/cc/test/test_image_factory.cc
+++ b/cc/test/test_image_factory.cc
@@ -4,6 +4,7 @@
 
 #include "cc/test/test_image_factory.h"
 
+#include "base/numerics/safe_conversions.h"
 #include "ui/gl/gl_image_shared_memory.h"
 
 namespace cc {
@@ -24,7 +25,8 @@ scoped_refptr<gl::GLImage> TestImageFactory::CreateImageForGpuMemoryBuffer(
 
   scoped_refptr<gl::GLImageSharedMemory> image(
       new gl::GLImageSharedMemory(size, internalformat));
-  if (!image->Initialize(handle.handle, handle.id, format, handle.offset))
+  if (!image->Initialize(handle.handle, handle.id, format, handle.offset,
+                         base::checked_cast<size_t>(handle.stride)))
     return nullptr;
 
   return image;
diff --git a/components/mus/gles2/command_buffer_driver.cc b/components/mus/gles2/command_buffer_driver.cc
index ba13464..2a7265a 100644
--- a/components/mus/gles2/command_buffer_driver.cc
+++ b/components/mus/gles2/command_buffer_driver.cc
@@ -26,6 +26,7 @@
 #include "gpu/command_buffer/service/valuebuffer_manager.h"
 #include "mojo/converters/geometry/geometry_type_converters.h"
 #include "mojo/platform_handle/platform_handle_functions.h"
+#include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gfx/vsync_provider.h"
 #include "ui/gl/gl_context.h"
@@ -250,7 +251,9 @@ void CommandBufferDriver::CreateImage(int32_t id,
   scoped_refptr<gl::GLImageSharedMemory> image =
       new gl::GLImageSharedMemory(gfx_size, internal_format);
   // TODO(jam): also need a mojo enum for this enum
-  if (!image->Initialize(handle, gfx::GpuMemoryBufferId(id), gpu_format, 0)) {
+  if (!image->Initialize(
+          handle, gfx::GpuMemoryBufferId(id), gpu_format, 0,
+          gfx::RowSizeForBufferFormat(gfx_size.width(), gpu_format, 0))) {
     NOTREACHED();
     return;
   }
diff --git a/components/mus/gles2/command_buffer_local.cc b/components/mus/gles2/command_buffer_local.cc
index 2e0535b..461262b 100644
--- a/components/mus/gles2/command_buffer_local.cc
+++ b/components/mus/gles2/command_buffer_local.cc
@@ -18,6 +18,7 @@
 #include "gpu/command_buffer/service/shader_translator_cache.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "gpu/command_buffer/service/valuebuffer_manager.h"
+#include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/vsync_provider.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_image_shared_memory.h"
@@ -139,7 +140,9 @@ int32_t CommandBufferLocal::CreateImage(ClientBuffer buffer,
       gfx::Size(static_cast<int>(width), static_cast<int>(height)),
       internalformat));
   if (!image->Initialize(base::SharedMemory::DuplicateHandle(handle.handle),
-                         handle.id, gpu_memory_buffer->GetFormat(), 0)) {
+                         handle.id, gpu_memory_buffer->GetFormat(), 0,
+                         gfx::RowSizeForBufferFormat(
+                             width, gpu_memory_buffer->GetFormat(), 0))) {
     return -1;
   }
 
diff --git a/components/mus/gles2/mojo_gpu_memory_buffer.cc b/components/mus/gles2/mojo_gpu_memory_buffer.cc
index dfa7d8b..597cd69 100644
--- a/components/mus/gles2/mojo_gpu_memory_buffer.cc
+++ b/components/mus/gles2/mojo_gpu_memory_buffer.cc
@@ -87,6 +87,7 @@ gfx::GpuMemoryBufferHandle MojoGpuMemoryBufferImpl::GetHandle() const {
   handle.type = gfx::SHARED_MEMORY_BUFFER;
   handle.handle = shared_memory_->handle();
   handle.offset = 0;
+  handle.stride = gfx::RowSizeForBufferFormat(size_.width(), format_, 0);
   return handle;
 }
 
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
index 7a5921a..b4b474e 100644
--- a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
+++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
@@ -546,6 +546,7 @@ void BrowserGpuMemoryBufferManager::HandleCreateGpuMemoryBufferFromHandleOnIO(
   handle.id = new_id;
   handle.handle = request->handle.handle;
   handle.offset = request->handle.offset;
+  handle.stride = request->handle.stride;
 
   // Note: Unretained is safe as IO thread is stopped before manager is
   // destroyed.
diff --git a/content/common/child_process_messages.h b/content/common/child_process_messages.h
index ec2a4bf..da73059 100644
--- a/content/common/child_process_messages.h
+++ b/content/common/child_process_messages.h
@@ -70,6 +70,7 @@ IPC_STRUCT_TRAITS_BEGIN(gfx::GpuMemoryBufferHandle)
   IPC_STRUCT_TRAITS_MEMBER(type)
   IPC_STRUCT_TRAITS_MEMBER(handle)
   IPC_STRUCT_TRAITS_MEMBER(offset)
+  IPC_STRUCT_TRAITS_MEMBER(stride)
 #if defined(USE_OZONE)
   IPC_STRUCT_TRAITS_MEMBER(native_pixmap_handle)
 #endif
diff --git a/content/common/gpu/client/gpu_channel_host.cc b/content/common/gpu/client/gpu_channel_host.cc
index 8ff3ab8..80dfe0a 100644
--- a/content/common/gpu/client/gpu_channel_host.cc
+++ b/content/common/gpu/client/gpu_channel_host.cc
@@ -372,6 +372,7 @@ gfx::GpuMemoryBufferHandle GpuChannelHost::ShareGpuMemoryBufferToGpuProcess(
       handle.type = gfx::SHARED_MEMORY_BUFFER;
       handle.handle = ShareToGpuProcess(source_handle.handle);
       handle.offset = source_handle.offset;
+      handle.stride = source_handle.stride;
       *requires_sync_point = false;
       return handle;
     }
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
index 9a36a3b..b7f750e 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
@@ -23,10 +23,12 @@ GpuMemoryBufferImplSharedMemory::GpuMemoryBufferImplSharedMemory(
     gfx::BufferFormat format,
     const DestructionCallback& callback,
     scoped_ptr<base::SharedMemory> shared_memory,
-    size_t offset)
+    size_t offset,
+    int stride)
     : GpuMemoryBufferImpl(id, size, format, callback),
       shared_memory_(shared_memory.Pass()),
-      offset_(offset) {
+      offset_(offset),
+      stride_(stride) {
   DCHECK(IsSizeValidForFormat(size, format));
 }
 
@@ -48,7 +50,8 @@ GpuMemoryBufferImplSharedMemory::Create(gfx::GpuMemoryBufferId id,
     return nullptr;
 
   return make_scoped_ptr(new GpuMemoryBufferImplSharedMemory(
-      id, size, format, callback, shared_memory.Pass(), 0));
+      id, size, format, callback, shared_memory.Pass(), 0,
+      gfx::RowSizeForBufferFormat(size.width(), format, 0)));
 }
 
 // static
@@ -70,6 +73,7 @@ GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
   handle.type = gfx::SHARED_MEMORY_BUFFER;
   handle.id = id;
   handle.offset = 0;
+  handle.stride = gfx::RowSizeForBufferFormat(size.width(), format, 0);
   shared_memory.GiveToProcess(child_process, &handle.handle);
   return handle;
 }
@@ -88,7 +92,7 @@ GpuMemoryBufferImplSharedMemory::CreateFromHandle(
   return make_scoped_ptr(new GpuMemoryBufferImplSharedMemory(
       handle.id, size, format, callback,
       make_scoped_ptr(new base::SharedMemory(handle.handle, false)),
-      handle.offset));
+      handle.offset, handle.stride));
 }
 
 // static
@@ -162,6 +166,7 @@ base::Closure GpuMemoryBufferImplSharedMemory::AllocateForTesting(
   DCHECK(rv);
   handle->type = gfx::SHARED_MEMORY_BUFFER;
   handle->offset = 0;
+  handle->stride = gfx::RowSizeForBufferFormat(size.width(), format, 0);
   handle->handle = base::SharedMemory::DuplicateHandle(shared_memory.handle());
   return base::Bind(&Noop);
 }
@@ -172,6 +177,8 @@ bool GpuMemoryBufferImplSharedMemory::Map() {
   // Map the buffer first time Map() is called then keep it mapped for the
   // lifetime of the buffer. This avoids mapping the buffer unless necessary.
   if (!shared_memory_->memory()) {
+    DCHECK_EQ(static_cast<size_t>(stride_),
+              gfx::RowSizeForBufferFormat(size_.width(), format_, 0));
     size_t buffer_size = gfx::BufferSizeForBufferFormat(size_, format_);
     // Note: offset_ != 0 is not common use-case. To keep it simple we
     // map offset + buffer_size here but this can be avoided using MapAt().
@@ -205,6 +212,7 @@ gfx::GpuMemoryBufferHandle GpuMemoryBufferImplSharedMemory::GetHandle() const {
   handle.type = gfx::SHARED_MEMORY_BUFFER;
   handle.id = id_;
   handle.offset = offset_;
+  handle.stride = stride_;
   handle.handle = shared_memory_->handle();
   return handle;
 }
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
index bb5f29b..22f292a 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
@@ -59,10 +59,12 @@ class CONTENT_EXPORT GpuMemoryBufferImplSharedMemory
                                   gfx::BufferFormat format,
                                   const DestructionCallback& callback,
                                   scoped_ptr<base::SharedMemory> shared_memory,
-                                  size_t offset);
+                                  size_t offset,
+                                  int stride);
 
   scoped_ptr<base::SharedMemory> shared_memory_;
   size_t offset_;
+  int stride_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferImplSharedMemory);
 };
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc
index 746bc11..f8559b9 100644
--- a/content/common/gpu/gpu_channel.cc
+++ b/content/common/gpu/gpu_channel.cc
@@ -17,6 +17,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/location.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
@@ -1040,10 +1041,14 @@ scoped_refptr<gl::GLImage> GpuChannel::CreateImageForGpuMemoryBuffer(
     uint32 internalformat) {
   switch (handle.type) {
     case gfx::SHARED_MEMORY_BUFFER: {
+      if (!base::IsValueInRangeForNumericType<size_t>(handle.stride))
+        return nullptr;
       scoped_refptr<gl::GLImageSharedMemory> image(
           new gl::GLImageSharedMemory(size, internalformat));
-      if (!image->Initialize(handle.handle, handle.id, format, handle.offset))
+      if (!image->Initialize(handle.handle, handle.id, format, handle.offset,
+                             handle.stride)) {
         return nullptr;
+      }
 
       return image;
     }
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc
index 5e1921a..3f22542 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.cc
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -15,6 +15,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/sequence_checker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/thread_task_runner_handle.h"
@@ -114,6 +115,7 @@ gfx::GpuMemoryBufferHandle ShareGpuMemoryBufferToGpuThread(
       handle.type = gfx::SHARED_MEMORY_BUFFER;
       handle.handle = ShareToGpuThread(source_handle.handle);
       handle.offset = source_handle.offset;
+      handle.stride = source_handle.stride;
       *requires_sync_point = false;
       return handle;
     }
@@ -735,9 +737,14 @@ void InProcessCommandBuffer::CreateImageOnGpuThread(
 
   switch (handle.type) {
     case gfx::SHARED_MEMORY_BUFFER: {
+      if (!base::IsValueInRangeForNumericType<size_t>(handle.stride)) {
+        LOG(ERROR) << "Invalid stride for image.";
+        return;
+      }
       scoped_refptr<gl::GLImageSharedMemory> image(
           new gl::GLImageSharedMemory(size, internalformat));
-      if (!image->Initialize(handle.handle, handle.id, format, handle.offset)) {
+      if (!image->Initialize(handle.handle, handle.id, format, handle.offset,
+                             handle.stride)) {
         LOG(ERROR) << "Failed to initialize image.";
         return;
       }
diff --git a/ui/gfx/gpu_memory_buffer.h b/ui/gfx/gpu_memory_buffer.h
index 217aae2..47d277e 100644
--- a/ui/gfx/gpu_memory_buffer.h
+++ b/ui/gfx/gpu_memory_buffer.h
@@ -39,6 +39,7 @@ struct GFX_EXPORT GpuMemoryBufferHandle {
   GpuMemoryBufferId id;
   base::SharedMemoryHandle handle;
   uint32_t offset;
+  int32_t stride;
 #if defined(USE_OZONE)
   NativePixmapHandle native_pixmap_handle;
 #endif
diff --git a/ui/gl/gl_image_memory.cc b/ui/gl/gl_image_memory.cc
index 03391ba..3bdf596 100644
--- a/ui/gl/gl_image_memory.cc
+++ b/ui/gl/gl_image_memory.cc
@@ -5,6 +5,7 @@
 #include "ui/gl/gl_image_memory.h"
 
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gl/gl_bindings.h"
@@ -71,9 +72,11 @@ bool IsCompressedFormat(BufferFormat format) {
     case BufferFormat::RGBA_8888:
     case BufferFormat::BGRX_8888:
     case BufferFormat::BGRA_8888:
+      return false;
     case BufferFormat::YUV_420:
     case BufferFormat::YUV_420_BIPLANAR:
     case BufferFormat::UYVY_422:
+      NOTREACHED();
       return false;
   }
 
@@ -129,10 +132,12 @@ GLenum DataFormat(BufferFormat format) {
     case BufferFormat::DXT1:
     case BufferFormat::DXT5:
     case BufferFormat::ETC1:
+      return TextureFormat(format);
     case BufferFormat::YUV_420:
     case BufferFormat::YUV_420_BIPLANAR:
     case BufferFormat::UYVY_422:
-      return TextureFormat(format);
+      NOTREACHED();
+      return 0;
   }
 
   NOTREACHED();
@@ -165,13 +170,42 @@ GLenum DataType(BufferFormat format) {
   return 0;
 }
 
+GLint DataRowLength(size_t stride, BufferFormat format) {
+  switch (format) {
+    case BufferFormat::RGBA_4444:
+      return base::checked_cast<GLint>(stride) / 2;
+    case BufferFormat::RGBX_8888:
+    case BufferFormat::RGBA_8888:
+    case BufferFormat::BGRX_8888:
+    case BufferFormat::BGRA_8888:
+      return base::checked_cast<GLint>(stride) / 4;
+    case BufferFormat::R_8:
+      return base::checked_cast<GLint>(stride);
+    case BufferFormat::ATC:
+    case BufferFormat::ATCIA:
+    case BufferFormat::DXT1:
+    case BufferFormat::DXT5:
+    case BufferFormat::ETC1:
+    case BufferFormat::YUV_420:
+    case BufferFormat::YUV_420_BIPLANAR:
+    case BufferFormat::UYVY_422:
+      NOTREACHED();
+      return 0;
+  }
+
+  NOTREACHED();
+  return 0;
+}
+
 template <typename F>
 scoped_ptr<uint8_t[]> GLES2RGBData(const gfx::Size& size,
                                    BufferFormat format,
+                                   size_t stride,
                                    const uint8_t* data,
                                    F const& data_to_rgb,
                                    GLenum* data_format,
-                                   GLenum* data_type) {
+                                   GLenum* data_type,
+                                   GLint* data_row_length) {
   TRACE_EVENT2("gpu", "GLES2RGBData", "width", size.width(), "height",
                size.height());
 
@@ -180,58 +214,78 @@ scoped_ptr<uint8_t[]> GLES2RGBData(const gfx::Size& size,
   size_t gles2_rgb_data_stride = (size.width() * 3 + 3) & ~3;
   scoped_ptr<uint8_t[]> gles2_rgb_data(
       new uint8_t[gles2_rgb_data_stride * size.height()]);
-  size_t data_stride = RowSizeForBufferFormat(size.width(), format, 0);
 
   for (int y = 0; y < size.height(); ++y) {
     for (int x = 0; x < size.width(); ++x) {
-      data_to_rgb(&data[y * data_stride + x * 4],
+      data_to_rgb(&data[y * stride + x * 4],
                   &gles2_rgb_data[y * gles2_rgb_data_stride + x * 3]);
     }
   }
 
   *data_format = GL_RGB;
   *data_type = GL_UNSIGNED_BYTE;
+  *data_row_length = size.width();
   return gles2_rgb_data.Pass();
 }
 
 scoped_ptr<uint8_t[]> GLES2Data(const gfx::Size& size,
                                 BufferFormat format,
+                                size_t stride,
                                 const uint8_t* data,
                                 GLenum* data_format,
-                                GLenum* data_type) {
+                                GLenum* data_type,
+                                GLint* data_row_length) {
+  TRACE_EVENT2("gpu", "GLES2Data", "width", size.width(), "height",
+               size.height());
+
   switch (format) {
     case BufferFormat::RGBX_8888:
-      return GLES2RGBData(size, format,
+      return GLES2RGBData(size, format, stride,
                           data, [](const uint8_t* src, uint8_t* dst) {
                             dst[0] = src[0];
                             dst[1] = src[1];
                             dst[2] = src[2];
-                          }, data_format, data_type);
+                          }, data_format, data_type, data_row_length);
     case BufferFormat::BGRX_8888:
-      return GLES2RGBData(size, format,
+      return GLES2RGBData(size, format, stride,
                           data, [](const uint8_t* src, uint8_t* dst) {
                             dst[0] = src[2];
                             dst[1] = src[1];
                             dst[2] = src[0];
-                          }, data_format, data_type);
+                          }, data_format, data_type, data_row_length);
     case BufferFormat::RGBA_4444:
     case BufferFormat::RGBA_8888:
     case BufferFormat::BGRA_8888:
-    case BufferFormat::R_8:
+    case BufferFormat::R_8: {
+      size_t gles2_data_stride =
+          RowSizeForBufferFormat(size.width(), format, 0);
+      if (stride == gles2_data_stride)
+        return nullptr;  // No data conversion needed
+
+      scoped_ptr<uint8_t[]> gles2_data(
+          new uint8_t[gles2_data_stride * size.height()]);
+      for (int y = 0; y < size.height(); ++y) {
+        memcpy(&gles2_data[y * gles2_data_stride], &data[y * stride],
+               gles2_data_stride);
+      }
+      *data_row_length = size.width();
+      return gles2_data.Pass();
+    }
     case BufferFormat::ATC:
     case BufferFormat::ATCIA:
     case BufferFormat::DXT1:
     case BufferFormat::DXT5:
     case BufferFormat::ETC1:
+      return nullptr;  // No data conversion needed
     case BufferFormat::YUV_420:
     case BufferFormat::YUV_420_BIPLANAR:
     case BufferFormat::UYVY_422:
-      // No data conversion needed.
+      NOTREACHED();
       return nullptr;
   }
 
   NOTREACHED();
-  return 0;
+  return nullptr;
 }
 
 }  // namespace
@@ -240,14 +294,16 @@ GLImageMemory::GLImageMemory(const gfx::Size& size, unsigned internalformat)
     : size_(size),
       internalformat_(internalformat),
       memory_(nullptr),
-      format_(BufferFormat::RGBA_8888) {}
+      format_(BufferFormat::RGBA_8888),
+      stride_(0) {}
 
 GLImageMemory::~GLImageMemory() {
   DCHECK(!memory_);
 }
 
 bool GLImageMemory::Initialize(const unsigned char* memory,
-                               BufferFormat format) {
+                               BufferFormat format,
+                               size_t stride) {
   if (!ValidInternalFormat(internalformat_)) {
     LOG(ERROR) << "Invalid internalformat: " << internalformat_;
     return false;
@@ -258,12 +314,18 @@ bool GLImageMemory::Initialize(const unsigned char* memory,
     return false;
   }
 
+  if (stride < RowSizeForBufferFormat(size_.width(), format, 0) || stride & 3) {
+    LOG(ERROR) << "Invalid stride: " << stride;
+    return false;
+  }
+
   DCHECK(memory);
   DCHECK(!memory_);
   DCHECK(!IsCompressedFormat(format) || size_.width() % 4 == 0);
   DCHECK(!IsCompressedFormat(format) || size_.height() % 4 == 0);
   memory_ = memory;
   format_ = format;
+  stride_ = stride;
   return true;
 }
 
@@ -297,16 +359,25 @@ bool GLImageMemory::CopyTexImage(unsigned target) {
         static_cast<GLsizei>(BufferSizeForBufferFormat(size_, format_)),
         memory_);
   } else {
-    scoped_ptr<uint8_t[]> gles2_data;
     GLenum data_format = DataFormat(format_);
     GLenum data_type = DataType(format_);
+    GLint data_row_length = DataRowLength(stride_, format_);
+    scoped_ptr<uint8_t[]> gles2_data;
+
+    if (gfx::GLContext::GetCurrent()->GetVersionInfo()->is_es) {
+      gles2_data = GLES2Data(size_, format_, stride_, memory_, &data_format,
+                             &data_type, &data_row_length);
+    }
 
-    if (gfx::GLContext::GetCurrent()->GetVersionInfo()->is_es)
-      gles2_data = GLES2Data(size_, format_, memory_, &data_format, &data_type);
+    if (data_row_length != size_.width())
+      glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length);
 
     glTexImage2D(target, 0, TextureFormat(format_), size_.width(),
                  size_.height(), 0, data_format, data_type,
                  gles2_data ? gles2_data.get() : memory_);
+
+    if (data_row_length != size_.width())
+      glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
   }
 
   return true;
@@ -326,13 +397,12 @@ bool GLImageMemory::CopyTexSubImage(unsigned target,
   if (rect.width() != size_.width())
     return false;
 
-  // Height must be a multiple of 4 if compressed.
-  if (IsCompressedFormat(format_) && rect.height() % 4)
-    return false;
-
-  const uint8_t* data =
-      memory_ + rect.y() * RowSizeForBufferFormat(size_.width(), format_, 0);
+  const uint8_t* data = memory_ + rect.y() * stride_;
   if (IsCompressedFormat(format_)) {
+    // Height must be a multiple of 4.
+    if (rect.height() % 4)
+      return false;
+
     glCompressedTexSubImage2D(
         target, 0, offset.x(), offset.y(), rect.width(), rect.height(),
         DataFormat(format_),
@@ -341,16 +411,23 @@ bool GLImageMemory::CopyTexSubImage(unsigned target,
   } else {
     GLenum data_format = DataFormat(format_);
     GLenum data_type = DataType(format_);
+    GLint data_row_length = DataRowLength(stride_, format_);
     scoped_ptr<uint8_t[]> gles2_data;
 
     if (gfx::GLContext::GetCurrent()->GetVersionInfo()->is_es) {
-      gles2_data =
-          GLES2Data(rect.size(), format_, data, &data_format, &data_type);
+      gles2_data = GLES2Data(rect.size(), format_, stride_, data, &data_format,
+                             &data_type, &data_row_length);
     }
 
+    if (data_row_length != rect.width())
+      glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length);
+
     glTexSubImage2D(target, 0, offset.x(), offset.y(), rect.width(),
                     rect.height(), data_format, data_type,
                     gles2_data ? gles2_data.get() : data);
+
+    if (data_row_length != rect.width())
+      glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
   }
 
   return true;
diff --git a/ui/gl/gl_image_memory.h b/ui/gl/gl_image_memory.h
index 3ce0440..5492ec3 100644
--- a/ui/gl/gl_image_memory.h
+++ b/ui/gl/gl_image_memory.h
@@ -16,7 +16,9 @@ class GL_EXPORT GLImageMemory : public GLImage {
  public:
   GLImageMemory(const gfx::Size& size, unsigned internalformat);
 
-  bool Initialize(const unsigned char* memory, gfx::BufferFormat format);
+  bool Initialize(const unsigned char* memory,
+                  gfx::BufferFormat format,
+                  size_t stride);
 
   // Overridden from GLImage:
   void Destroy(bool have_context) override;
@@ -40,12 +42,14 @@ class GL_EXPORT GLImageMemory : public GLImage {
   ~GLImageMemory() override;
 
   gfx::BufferFormat format() const { return format_; }
+  size_t stride() const { return stride_; }
 
  private:
   const gfx::Size size_;
   const unsigned internalformat_;
   const unsigned char* memory_;
   gfx::BufferFormat format_;
+  size_t stride_;
 
   DISALLOW_COPY_AND_ASSIGN(GLImageMemory);
 };
diff --git a/ui/gl/gl_image_ref_counted_memory.cc b/ui/gl/gl_image_ref_counted_memory.cc
index a417f15..1831c08 100644
--- a/ui/gl/gl_image_ref_counted_memory.cc
+++ b/ui/gl/gl_image_ref_counted_memory.cc
@@ -9,6 +9,7 @@
 #include "base/trace_event/memory_allocator_dump.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/process_memory_dump.h"
+#include "ui/gfx/buffer_format_util.h"
 
 namespace gl {
 
@@ -23,8 +24,11 @@ GLImageRefCountedMemory::~GLImageRefCountedMemory() {
 bool GLImageRefCountedMemory::Initialize(
     base::RefCountedMemory* ref_counted_memory,
     gfx::BufferFormat format) {
-  if (!GLImageMemory::Initialize(ref_counted_memory->front(), format))
+  if (!GLImageMemory::Initialize(
+          ref_counted_memory->front(), format,
+          gfx::RowSizeForBufferFormat(GetSize().width(), format, 0))) {
     return false;
+  }
 
   DCHECK(!ref_counted_memory_.get());
   ref_counted_memory_ = ref_counted_memory;
diff --git a/ui/gl/gl_image_shared_memory.cc b/ui/gl/gl_image_shared_memory.cc
index 6ea5553..21c72c1 100644
--- a/ui/gl/gl_image_shared_memory.cc
+++ b/ui/gl/gl_image_shared_memory.cc
@@ -28,9 +28,14 @@ bool GLImageSharedMemory::Initialize(
     const base::SharedMemoryHandle& handle,
     gfx::GenericSharedMemoryId shared_memory_id,
     gfx::BufferFormat format,
-    size_t offset) {
-  size_t size_in_bytes;
-  if (!BufferSizeForBufferFormatChecked(GetSize(), format, &size_in_bytes))
+    size_t offset,
+    size_t stride) {
+  if (NumberOfPlanesForBufferFormat(format) != 1)
+    return false;
+
+  base::CheckedNumeric<size_t> checked_size = stride;
+  checked_size *= GetSize().height();
+  if (!checked_size.IsValid())
     return false;
 
   if (!base::SharedMemory::IsHandleValid(handle))
@@ -52,22 +57,21 @@ bool GLImageSharedMemory::Initialize(
   size_t map_offset = base::SysInfo::VMAllocationGranularity() *
                       (offset / base::SysInfo::VMAllocationGranularity());
 
-  base::CheckedNumeric<size_t> checked_size_to_map_in_bytes = size_in_bytes;
-  checked_size_to_map_in_bytes += memory_offset;
-  if (!checked_size_to_map_in_bytes.IsValid())
+  checked_size += memory_offset;
+  if (!checked_size.IsValid())
     return false;
 
   scoped_ptr<base::SharedMemory> duped_shared_memory(
       new base::SharedMemory(duped_shared_memory_handle, true));
   if (!duped_shared_memory->MapAt(static_cast<off_t>(map_offset),
-                                  checked_size_to_map_in_bytes.ValueOrDie())) {
+                                  checked_size.ValueOrDie())) {
     DVLOG(0) << "Failed to map shared memory.";
     return false;
   }
 
   if (!GLImageMemory::Initialize(
           static_cast<uint8_t*>(duped_shared_memory->memory()) + memory_offset,
-          format)) {
+          format, stride)) {
     return false;
   }
 
@@ -89,7 +93,7 @@ void GLImageSharedMemory::OnMemoryDump(
   size_t size_in_bytes = 0;
 
   if (shared_memory_)
-    size_in_bytes = BufferSizeForBufferFormat(GetSize(), format());
+    size_in_bytes = stride() * GetSize().height();
 
   // Dump under "/shared_memory", as the base class may also dump to
   // "/texture_memory".
diff --git a/ui/gl/gl_image_shared_memory.h b/ui/gl/gl_image_shared_memory.h
index e3425d2..3c1564e 100644
--- a/ui/gl/gl_image_shared_memory.h
+++ b/ui/gl/gl_image_shared_memory.h
@@ -23,7 +23,8 @@ class GL_EXPORT GLImageSharedMemory : public GLImageMemory {
   bool Initialize(const base::SharedMemoryHandle& handle,
                   gfx::GenericSharedMemoryId shared_memory_id,
                   gfx::BufferFormat format,
-                  size_t offset);
+                  size_t offset,
+                  size_t stride);
 
   // Overridden from GLImage:
   void Destroy(bool have_context) override;
diff --git a/ui/gl/gl_image_shared_memory_unittest.cc b/ui/gl/gl_image_shared_memory_unittest.cc
index 2769960..da6feac 100644
--- a/ui/gl/gl_image_shared_memory_unittest.cc
+++ b/ui/gl/gl_image_shared_memory_unittest.cc
@@ -30,7 +30,8 @@ class GLImageSharedMemoryTestDelegate {
         size, gl::GLImageMemory::GetInternalFormatForTesting(format)));
     rv = image->Initialize(
         base::SharedMemory::DuplicateHandle(shared_memory.handle()),
-        gfx::GenericSharedMemoryId(0), format, 0);
+        gfx::GenericSharedMemoryId(0), format, 0,
+        gfx::RowSizeForBufferFormat(size.width(), format, 0));
     EXPECT_TRUE(rv);
     return image;
   }
@@ -55,10 +56,13 @@ class GLImageSharedMemoryPoolTestDelegate {
   scoped_refptr<gl::GLImage> CreateSolidColorImage(
       const gfx::Size& size,
       const uint8_t color[4]) const {
-    // Create a shared memory segment that is 2 pages larger than image.
+    // Create a shared memory segment that holds an image with a stride that is
+    // twice the row size and 2 pages larger than image.
+    size_t stride = gfx::RowSizeForBufferFormat(
+                        size.width(), gfx::BufferFormat::RGBA_8888, 0) *
+                    2;
     size_t pool_size =
-        gfx::BufferSizeForBufferFormat(size, gfx::BufferFormat::RGBA_8888) +
-        base::SysInfo::VMAllocationGranularity() * 3;
+        stride * size.height() + base::SysInfo::VMAllocationGranularity() * 3;
     base::SharedMemory shared_memory;
     bool rv = shared_memory.CreateAndMapAnonymous(pool_size);
     DCHECK(rv);
@@ -67,9 +71,7 @@ class GLImageSharedMemoryPoolTestDelegate {
     // Place buffer at a non-zero non-page-aligned offset in shared memory.
     size_t buffer_offset = 3 * base::SysInfo::VMAllocationGranularity() / 2;
     GLImageTestSupport::SetBufferDataToColor(
-        size.width(), size.height(),
-        static_cast<int>(RowSizeForBufferFormat(
-            size.width(), gfx::BufferFormat::RGBA_8888, 0)),
+        size.width(), size.height(), static_cast<int>(stride),
         gfx::BufferFormat::RGBA_8888, color,
         reinterpret_cast<uint8_t*>(shared_memory.memory()) + buffer_offset);
     scoped_refptr<gl::GLImageSharedMemory> image(
@@ -77,7 +79,7 @@ class GLImageSharedMemoryPoolTestDelegate {
     rv = image->Initialize(
         base::SharedMemory::DuplicateHandle(shared_memory.handle()),
         gfx::GenericSharedMemoryId(0), gfx::BufferFormat::RGBA_8888,
-        buffer_offset);
+        buffer_offset, stride);
     EXPECT_TRUE(rv);
     return image;
   }
-- 
cgit v1.1