Merge from Chromium at DEPS revision r202854

This commit was generated by merge_to_master.py.

Change-Id: Idca323f71ef844a9e04f454d4f070b1e398f2deb
diff --git a/gpu/DEPS b/gpu/DEPS
index 43b6a19..074d493 100644
--- a/gpu/DEPS
+++ b/gpu/DEPS
@@ -5,12 +5,6 @@
   "+third_party/re2",
   "+third_party/smhasher",
   "+third_party/protbuf",
-  "+../../gpu_export.h",
-  "+../command_buffer",
-  "+../client",
-  "+../common",
-  "+../GLES2",
-  "+../service",
   "+crypto",
   "+ui/gfx",
   "+ui/gl",
diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_map_image.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_map_image.txt
new file mode 100644
index 0000000..6304fda
--- /dev/null
+++ b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_map_image.txt
@@ -0,0 +1,107 @@
+Name
+
+    CHROMIUM_map_image
+
+Name Strings
+
+    GL_CHROMIUM_map_image
+
+Version
+
+    Last Modifed Date: May 9, 2013
+
+Dependencies
+
+    OpenGL ES 2.0 is required.
+
+Overview
+
+    This extension allows for more efficient uploading of texture data through
+    Chromium's OpenGL ES 2.0 implementation.
+
+    For security reasons Chromium accesses the GPU from a separate process. User
+    processes are not allowed to access the GPU directly. This multi-process
+    architechure has the advantage that GPU operations can be secured and
+    pipelined but it has the disadvantage that all data that is going to be
+    passed to GPU must first be made available to the separate GPU process.
+
+    This extension helps the application directly allocate and access texture
+    memory.
+
+Issues
+
+    None
+
+New Tokens
+
+    None
+
+New Procedures and Functions
+
+    GLuint CreateImageCHROMIUM (GLsizei width, GLsizei height,
+                                GLenum internalformat)
+
+    Allocate an image with width equal to <width> and height equal
+    to <height> stored in format <internalformat>.
+
+    Returns a unique identifier for the allocated image that could be used
+    in subsequent operations.
+
+    INVALID_VALUE is generated if <width> or <height> is nonpositive.
+
+    void DestroyImageCHROMIUM (GLuint image_id)
+
+    Frees the image previously allocated by a call to CreateImageCHROMIUM.
+
+    INVALID_OPERATION is generated if <image_id> is not a valid image id.
+
+    void* MapImageCHROMIUM (GLuint image_id, GLenum access)
+
+    Returns a pointer to in the user memory for the application to modify
+    the image. <access> parameter defines if the user will read or write the
+    pixels.
+
+    INVALID_OPERATION is generated if <image_id> is not a valid image id.
+
+    INVALID_OPERATION is generated if the image was already mapped by a previous
+    call to this method.
+
+    INVALID_ENUM is generated if <access> is not one of WRITE_ONLY, READ_ONLY
+    and READ_WRITE.
+
+    void UnmapImageCHROMIUM (GLuint image_id)
+
+    Removes the mapping created by a call to MapImageCHROMIUM.
+
+    Note that after calling UnmapImageCHROMIUM the application should assume
+    that the memory returned by MapImageCHROMIUM is off limits and is no longer
+    accessible by the application. Accessing it after calling
+    UnmapImageCHROMIUM will produce undefined results.
+
+    INVALID_OPERATION is generated if <image_id> is not a valid image id.
+
+    INVALID_OPERATION is generated if the image was not already mapped by a
+    previous call to MapImageCHROMIUM.
+
+    void GetImageParameterivCHROMIUM(GLuint image_id, GLenum pname,
+                                     GLint* params)
+
+    Sets <params> to the integer value of the parameter specified by <pname>
+    for the image specified by <image_id>. <params> is expected to be
+    properly allocated before calling this method.
+
+    INVALID_OPERATION is generated if <image_id> is not a valid image id.
+
+    INVALID_ENUM is generated if <pname> is not IMAGE_ROWBYTES_CHROMIUM.
+
+Errors
+
+    None.
+
+New State
+
+    None.
+
+Revision History
+
+    5/9/2013    Documented the extension
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index fa099e6..e8970e0 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -182,6 +182,8 @@
 #define glEnableFeatureCHROMIUM GLES2_GET_FUN(EnableFeatureCHROMIUM)
 #define glMapBufferCHROMIUM GLES2_GET_FUN(MapBufferCHROMIUM)
 #define glUnmapBufferCHROMIUM GLES2_GET_FUN(UnmapBufferCHROMIUM)
+#define glMapImageCHROMIUM GLES2_GET_FUN(MapImageCHROMIUM)
+#define glUnmapImageCHROMIUM GLES2_GET_FUN(UnmapImageCHROMIUM)
 #define glMapBufferSubDataCHROMIUM GLES2_GET_FUN(MapBufferSubDataCHROMIUM)
 #define glUnmapBufferSubDataCHROMIUM GLES2_GET_FUN(UnmapBufferSubDataCHROMIUM)
 #define glMapTexSubImage2DCHROMIUM GLES2_GET_FUN(MapTexSubImage2DCHROMIUM)
@@ -199,6 +201,10 @@
     CreateStreamTextureCHROMIUM)
 #define glDestroyStreamTextureCHROMIUM GLES2_GET_FUN( \
     DestroyStreamTextureCHROMIUM)
+#define glCreateImageCHROMIUM GLES2_GET_FUN(CreateImageCHROMIUM)
+#define glDestroyImageCHROMIUM GLES2_GET_FUN(DestroyImageCHROMIUM)
+#define glGetImageParameterivCHROMIUM GLES2_GET_FUN( \
+    GetImageParameterivCHROMIUM)
 #define glGetTranslatedShaderSourceANGLE GLES2_GET_FUN( \
     GetTranslatedShaderSourceANGLE)
 #define glPostSubBufferCHROMIUM GLES2_GET_FUN(PostSubBufferCHROMIUM)
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h
index 9193467..2568074 100644
--- a/gpu/GLES2/gl2extchromium.h
+++ b/gpu/GLES2/gl2extchromium.h
@@ -94,6 +94,39 @@
 #endif
 #endif  /* GL_CHROMIUM_pixel_transfer_buffer_object */
 
+/* GL_CHROMIUM_map_image */
+#ifndef GL_CHROMIUM_map_image
+#define GL_CHROMIUM_map_image 1
+
+#ifndef GL_IMAGE_ROWBYTES_CHROMIUM
+#define GL_IMAGE_ROWBYTES_CHROMIUM 0x78F0
+#endif
+
+#ifndef GL_READ_WRITE
+#define GL_READ_WRITE 0x88BA
+#endif
+
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL GLuint GL_APIENTRY glCreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat);
+GL_APICALL void GL_APIENTRY glDestroyImageCHROMIUM(GLuint image_id);
+GL_APICALL void GL_APIENTRY glGetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params);
+GL_APICALL void* GL_APIENTRY glMapImageCHROMIUM(GLuint image_id, GLenum access);
+GL_APICALL void GL_APIENTRY glUnmapImageCHROMIUM(GLuint image_id);
+#endif
+typedef GLuint (GL_APIENTRYP PFNGLCREATEIMAGECHROMIUMPROC) (
+    GLsizei width, GLsizei height, GLenum internalformat);
+typedef void (
+    GL_APIENTRYP PFNGLDESTROYIMAGECHROMIUMPROC) (GLuint image_id);
+typedef void (
+    GL_APIENTRYP PFNGLGETIMAGEPARAMETERIVCHROMIUMPROC) (
+    GLuint image_id, GLenum pname, GLint* params);
+typedef void* (GL_APIENTRYP PFNGLMAPIMAGECHROMIUMPROC) (
+    GLuint image_id, GLenum access);
+typedef void (GL_APIENTRYP PFNGLUNMAPIMAGECHROMIUMPROC) (GLuint image_id);
+#endif  /* GL_CHROMIUM_map_image */
+
 /* GL_CHROMIUM_map_sub */
 #ifndef GL_CHROMIUM_map_sub
 #define GL_CHROMIUM_map_sub 1
@@ -542,7 +575,8 @@
 #ifndef GL_CHROMIUM_resize
 #define GL_CHROMIUM_resize 1
 #ifdef GL_GLEXT_PROTOTYPES
-GL_APICALL void GL_APIENTRY glResizeCHROMIUM(GLuint width, GLuint height);
+GL_APICALL void GL_APIENTRY glResizeCHROMIUM(
+    GLuint width, GLuint height, GLfloat scale_factor);
 #endif
 typedef void (GL_APIENTRYP PFNGLRESIZECHROMIUMPROC) (
     GLuint width, GLuint height);
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index f8a355d..435d13a 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1306,6 +1306,32 @@
     'decoder_func': 'DoCopyTexSubImage2D',
     'defer_reads': True,
   },
+  'CreateImageCHROMIUM': {
+    'type': 'Manual',
+    'cmd_args': 'GLsizei width, GLsizei height, GLenum internalformat',
+    'result': ['GLuint'],
+    'client_test': False,
+    'gen_cmd': False,
+    'expectation': False,
+    'extension': True,
+    'chromium': True,
+  },
+  'DestroyImageCHROMIUM': {
+    'type': 'Manual',
+    'immediate': True,
+    'client_test': False,
+    'gen_cmd': False,
+    'extension': True,
+    'chromium': True,
+  },
+  'GetImageParameterivCHROMIUM': {
+    'type': 'Manual',
+    'client_test': False,
+    'gen_cmd': False,
+    'expectation': False,
+    'extension': True,
+    'chromium': True,
+  },
   'CreateProgram': {
     'type': 'Create',
     'client_test': False,
@@ -1780,7 +1806,6 @@
     'extension': True,
     'chromium': True,
     'client_test': False,
-    'chromium': True,
   },
   'MapBufferSubDataCHROMIUM': {
     'gen_cmd': False,
@@ -1789,6 +1814,12 @@
     'client_test': False,
     'pepper_interface': 'ChromiumMapSub',
   },
+  'MapImageCHROMIUM': {
+    'gen_cmd': False,
+    'extension': True,
+    'chromium': True,
+    'client_test': False,
+  },
   'MapTexSubImage2DCHROMIUM': {
     'gen_cmd': False,
     'extension': True,
@@ -2015,7 +2046,6 @@
     'extension': True,
     'chromium': True,
     'client_test': False,
-    'chromium': True,
   },
   'UnmapBufferSubDataCHROMIUM': {
     'gen_cmd': False,
@@ -2024,6 +2054,12 @@
     'client_test': False,
     'pepper_interface': 'ChromiumMapSub',
   },
+  'UnmapImageCHROMIUM': {
+    'gen_cmd': False,
+    'extension': True,
+    'chromium': True,
+    'client_test': False,
+  },
   'UnmapTexSubImage2DCHROMIUM': {
     'gen_cmd': False,
     'extension': True,
diff --git a/gpu/command_buffer/client/atomicops.cc b/gpu/command_buffer/client/atomicops.cc
index 318c337..c4d5cb1 100644
--- a/gpu/command_buffer/client/atomicops.cc
+++ b/gpu/command_buffer/client/atomicops.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/atomicops.h"
-#include "../common/logging.h"
+#include "gpu/command_buffer/client/atomicops.h"
+#include "gpu/command_buffer/common/logging.h"
 
 #if !defined(__native_client__)
 #include "base/atomicops.h"
diff --git a/gpu/command_buffer/client/atomicops.h b/gpu/command_buffer/client/atomicops.h
index 8a6f979..9e1628e 100644
--- a/gpu/command_buffer/client/atomicops.h
+++ b/gpu/command_buffer/client/atomicops.h
@@ -6,8 +6,8 @@
 #define GPU_COMMAND_BUFFER_CLIENT_ATOMICOPS_H_
 
 #include "base/memory/scoped_ptr.h"
-#include "../../gpu_export.h"
-#include "../common/types.h"
+#include "gpu/command_buffer/common/types.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/buffer_tracker.cc b/gpu/command_buffer/client/buffer_tracker.cc
index 59c7d1e..f5ead5a 100644
--- a/gpu/command_buffer/client/buffer_tracker.cc
+++ b/gpu/command_buffer/client/buffer_tracker.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/buffer_tracker.h"
+#include "gpu/command_buffer/client/buffer_tracker.h"
 
-#include "../client/atomicops.h"
-#include "../client/cmd_buffer_helper.h"
-#include "../client/mapped_memory.h"
+#include "gpu/command_buffer/client/atomicops.h"
+#include "gpu/command_buffer/client/cmd_buffer_helper.h"
+#include "gpu/command_buffer/client/mapped_memory.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/buffer_tracker.h b/gpu/command_buffer/client/buffer_tracker.h
index f8fc057..e61c501 100644
--- a/gpu/command_buffer/client/buffer_tracker.h
+++ b/gpu/command_buffer/client/buffer_tracker.h
@@ -8,9 +8,9 @@
 #include <GLES2/gl2.h>
 
 #include <queue>
-#include "../client/hash_tables.h"
-#include "../common/gles2_cmd_format.h"
 #include "gles2_impl_export.h"
+#include "gpu/command_buffer/client/hash_tables.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/client_context_state.cc b/gpu/command_buffer/client/client_context_state.cc
index 84a262e..ecb33b6 100644
--- a/gpu/command_buffer/client/client_context_state.cc
+++ b/gpu/command_buffer/client/client_context_state.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/client_context_state.h"
-#include "../common/logging.h"
+#include "gpu/command_buffer/client/client_context_state.h"
+#include "gpu/command_buffer/common/logging.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.cc b/gpu/command_buffer/client/cmd_buffer_helper.cc
index b68c11f..2fa15aa 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper.cc
@@ -4,9 +4,9 @@
 
 // This file contains the implementation of the command buffer helper class.
 
-#include "../client/cmd_buffer_helper.h"
-#include "../common/command_buffer.h"
-#include "../common/trace_event.h"
+#include "gpu/command_buffer/client/cmd_buffer_helper.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/trace_event.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.h b/gpu/command_buffer/client/cmd_buffer_helper.h
index 938dfdf..5ef8d0e 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper.h
+++ b/gpu/command_buffer/client/cmd_buffer_helper.h
@@ -10,11 +10,11 @@
 #include <string.h>
 #include <time.h>
 
-#include "../../gpu_export.h"
-#include "../common/logging.h"
-#include "../common/constants.h"
-#include "../common/cmd_buffer_common.h"
-#include "../common/command_buffer.h"
+#include "gpu/command_buffer/common/cmd_buffer_common.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/common/logging.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/fenced_allocator.cc b/gpu/command_buffer/client/fenced_allocator.cc
index 7e1ab2f..6eb9ab3 100644
--- a/gpu/command_buffer/client/fenced_allocator.cc
+++ b/gpu/command_buffer/client/fenced_allocator.cc
@@ -4,9 +4,9 @@
 
 // This file contains the implementation of the FencedAllocator class.
 
-#include "../client/fenced_allocator.h"
+#include "gpu/command_buffer/client/fenced_allocator.h"
 #include <algorithm>
-#include "../client/cmd_buffer_helper.h"
+#include "gpu/command_buffer/client/cmd_buffer_helper.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/fenced_allocator.h b/gpu/command_buffer/client/fenced_allocator.h
index 3ccad45..90288d9 100644
--- a/gpu/command_buffer/client/fenced_allocator.h
+++ b/gpu/command_buffer/client/fenced_allocator.h
@@ -9,9 +9,9 @@
 
 #include <vector>
 
-#include "../../gpu_export.h"
-#include "../common/logging.h"
-#include "../common/types.h"
+#include "gpu/command_buffer/common/logging.h"
+#include "gpu/command_buffer/common/types.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 class CommandBufferHelper;
diff --git a/gpu/command_buffer/client/gles2_c_lib.cc b/gpu/command_buffer/client/gles2_c_lib.cc
index ea20739..fbe7b55 100644
--- a/gpu/command_buffer/client/gles2_c_lib.cc
+++ b/gpu/command_buffer/client/gles2_c_lib.cc
@@ -6,7 +6,7 @@
 
 #include <assert.h>
 #include <stdlib.h>
-#include "../client/gles2_lib.h"
+#include "gpu/command_buffer/client/gles2_lib.h"
 
 #ifndef GL_GLEXT_PROTOTYPES
 #define GL_GLEXT_PROTOTYPES
@@ -18,7 +18,7 @@
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
 // instead of having to edit some template or the code generator.
-#include "../client/gles2_c_lib_autogen.h"
+#include "gpu/command_buffer/client/gles2_c_lib_autogen.h"
 }  // extern "C"
 
 
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index f1a5b08..f617163 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -595,6 +595,12 @@
 GLboolean GLES2UnmapBufferCHROMIUM(GLuint target) {
   return gles2::GetGLContext()->UnmapBufferCHROMIUM(target);
 }
+void* GLES2MapImageCHROMIUM(GLuint image_id, GLenum access) {
+  return gles2::GetGLContext()->MapImageCHROMIUM(image_id, access);
+}
+void GLES2UnmapImageCHROMIUM(GLuint image_id) {
+  gles2::GetGLContext()->UnmapImageCHROMIUM(image_id);
+}
 void* GLES2MapBufferSubDataCHROMIUM(
     GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) {
   return gles2::GetGLContext()->MapBufferSubDataCHROMIUM(
@@ -612,8 +618,8 @@
 void GLES2UnmapTexSubImage2DCHROMIUM(const void* mem) {
   gles2::GetGLContext()->UnmapTexSubImage2DCHROMIUM(mem);
 }
-void GLES2ResizeCHROMIUM(GLuint width, GLuint height) {
-  gles2::GetGLContext()->ResizeCHROMIUM(width, height);
+void GLES2ResizeCHROMIUM(GLuint width, GLuint height, GLfloat scale_factor) {
+  gles2::GetGLContext()->ResizeCHROMIUM(width, height, scale_factor);
 }
 const GLchar* GLES2GetRequestableExtensionsCHROMIUM() {
   return gles2::GetGLContext()->GetRequestableExtensionsCHROMIUM();
@@ -639,6 +645,18 @@
 void GLES2DestroyStreamTextureCHROMIUM(GLuint texture) {
   gles2::GetGLContext()->DestroyStreamTextureCHROMIUM(texture);
 }
+GLuint GLES2CreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat) {
+  return gles2::GetGLContext()->CreateImageCHROMIUM(
+      width, height, internalformat);
+}
+void GLES2DestroyImageCHROMIUM(GLuint image_id) {
+  gles2::GetGLContext()->DestroyImageCHROMIUM(image_id);
+}
+void GLES2GetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params) {
+  gles2::GetGLContext()->GetImageParameterivCHROMIUM(image_id, pname, params);
+}
 void GLES2GetTranslatedShaderSourceANGLE(
     GLuint shader, GLsizei bufsize, GLsizei* length, char* source) {
   gles2::GetGLContext()->GetTranslatedShaderSourceANGLE(
@@ -1006,6 +1024,10 @@
       glMapBufferCHROMIUM), },
   { "glUnmapBufferCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
       glUnmapBufferCHROMIUM), },
+  { "glMapImageCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
+      glMapImageCHROMIUM), },
+  { "glUnmapImageCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
+      glUnmapImageCHROMIUM), },
   { "glMapBufferSubDataCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
       glMapBufferSubDataCHROMIUM), },
   { "glUnmapBufferSubDataCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
@@ -1028,6 +1050,12 @@
       glCreateStreamTextureCHROMIUM), },
   { "glDestroyStreamTextureCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
       glDestroyStreamTextureCHROMIUM), },
+  { "glCreateImageCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
+      glCreateImageCHROMIUM), },
+  { "glDestroyImageCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
+      glDestroyImageCHROMIUM), },
+  { "glGetImageParameterivCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
+      glGetImageParameterivCHROMIUM), },
   { "glGetTranslatedShaderSourceANGLE", reinterpret_cast<GLES2FunctionPointer>(
       glGetTranslatedShaderSourceANGLE), },
   { "glPostSubBufferCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(
diff --git a/gpu/command_buffer/client/gles2_cmd_helper.cc b/gpu/command_buffer/client/gles2_cmd_helper.cc
index 8264941..d52970a 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper.cc
+++ b/gpu/command_buffer/client/gles2_cmd_helper.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/gles2_cmd_helper.h b/gpu/command_buffer/client/gles2_cmd_helper.h
index 521d864..7361a9b 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper.h
@@ -5,9 +5,9 @@
 #ifndef GPU_COMMAND_BUFFER_CLIENT_GLES2_CMD_HELPER_H_
 #define GPU_COMMAND_BUFFER_CLIENT_GLES2_CMD_HELPER_H_
 
-#include "../../gpu_export.h"
-#include "../client/cmd_buffer_helper.h"
-#include "../common/gles2_cmd_format.h"
+#include "gpu/command_buffer/client/cmd_buffer_helper.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 namespace gles2 {
@@ -21,7 +21,7 @@
   // Include the auto-generated part of this class. We split this because it
   // means we can easily edit the non-auto generated parts right here in this
   // file instead of having to edit some template or the code generator.
-  #include "../client/gles2_cmd_helper_autogen.h"
+  #include "gpu/command_buffer/client/gles2_cmd_helper_autogen.h"
 
   // Helpers that could not be auto-generated.
   // TODO(gman): Auto generate these.
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index 1a99879..a290365 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -1777,11 +1777,11 @@
     }
   }
 
-  void ResizeCHROMIUM(GLuint width, GLuint height) {
+  void ResizeCHROMIUM(GLuint width, GLuint height, GLfloat scale_factor) {
     gles2::cmds::ResizeCHROMIUM* c =
         GetCmdSpace<gles2::cmds::ResizeCHROMIUM>();
     if (c) {
-      c->Init(width, height);
+      c->Init(width, height, scale_factor);
     }
   }
 
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index 0ce55a5..b64c67b 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -4,7 +4,7 @@
 
 // A class to emulate GLES2 over command buffers.
 
-#include "../client/gles2_implementation.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
 
 #include <algorithm>
 #include <map>
@@ -15,14 +15,16 @@
 #include <string.h>
 #include <GLES2/gl2ext.h>
 #include <GLES2/gl2extchromium.h>
-#include "../client/buffer_tracker.h"
-#include "../client/mapped_memory.h"
-#include "../client/program_info_manager.h"
-#include "../client/query_tracker.h"
-#include "../client/transfer_buffer.h"
-#include "../client/vertex_array_object_manager.h"
-#include "../common/gles2_cmd_utils.h"
-#include "../common/trace_event.h"
+#include "gpu/command_buffer/client/buffer_tracker.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_tracker.h"
+#include "gpu/command_buffer/client/mapped_memory.h"
+#include "gpu/command_buffer/client/program_info_manager.h"
+#include "gpu/command_buffer/client/query_tracker.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/command_buffer/client/vertex_array_object_manager.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "gpu/command_buffer/common/trace_event.h"
 
 #if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
 #define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
@@ -84,7 +86,8 @@
       ShareGroup* share_group,
       TransferBufferInterface* transfer_buffer,
       bool share_resources,
-      bool bind_generates_resource)
+      bool bind_generates_resource,
+      ImageFactory* image_factory)
     : helper_(helper),
       transfer_buffer_(transfer_buffer),
       angle_pack_reverse_row_order_status_(kUnknownExtensionStatus),
@@ -108,7 +111,8 @@
       debug_(false),
       use_count_(0),
       current_query_(NULL),
-      error_message_callback_(NULL) {
+      error_message_callback_(NULL),
+      image_factory_(image_factory) {
   GPU_DCHECK(helper);
   GPU_DCHECK(transfer_buffer);
 
@@ -162,6 +166,7 @@
 
   query_tracker_.reset(new QueryTracker(mapped_memory_.get()));
   buffer_tracker_.reset(new BufferTracker(mapped_memory_.get()));
+  gpu_memory_buffer_tracker_.reset(new GpuMemoryBufferTracker(image_factory_));
 
 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
   GetIdHandler(id_namespaces::kBuffers)->MakeIds(
@@ -2094,6 +2099,10 @@
             "GL_CHROMIUM_map_sub "
             "GL_CHROMIUM_shallow_flush "
             "GL_EXT_unpack_subimage";
+        if (image_factory_ != NULL) {
+          // The first space character is intentional.
+          str += " GL_CHROMIUM_map_image";
+        }
         break;
       default:
         break;
@@ -2227,7 +2236,7 @@
     GLuint offset = ToGLuint(pixels);
     BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid(
         bound_pixel_pack_transfer_buffer_id_,
-        "glReadPixels", offset, temp_size);
+        "glReadPixels", offset, padded_row_size * height);
     if (buffer && buffer->shm_id() != -1) {
       helper_->ReadPixels(xoffset, yoffset, width, height, format, type,
                           buffer->shm_id(), buffer->shm_offset(),
@@ -2858,11 +2867,12 @@
   CheckGLError();
 }
 
-void GLES2Implementation::ResizeCHROMIUM(GLuint width, GLuint height) {
+void GLES2Implementation::ResizeCHROMIUM(GLuint width, GLuint height,
+                                         float scale_factor) {
   GPU_CLIENT_SINGLE_THREAD_CHECK();
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glResizeCHROMIUM("
-                 << width << ", " << height << ")");
-  helper_->ResizeCHROMIUM(width, height);
+                 << width << ", " << height << ", " << scale_factor << ")");
+  helper_->ResizeCHROMIUM(width, height, scale_factor);
   CheckGLError();
 }
 
@@ -3567,11 +3577,11 @@
   }
   BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id);
   if (!buffer) {
-    SetGLError(GL_INVALID_OPERATION, "glMapBufferCHROMIUM", "invalid buffer");
+    SetGLError(GL_INVALID_OPERATION, "glUnmapBufferCHROMIUM", "invalid buffer");
     return false;
   }
   if (!buffer->mapped()) {
-    SetGLError(GL_INVALID_OPERATION, "glMapBufferCHROMIUM", "not mapped");
+    SetGLError(GL_INVALID_OPERATION, "glUnmapBufferCHROMIUM", "not mapped");
     return false;
   }
   buffer->set_mapped(false);
@@ -3687,10 +3697,172 @@
   return helper_->InsertSyncPointCHROMIUM();
 }
 
+GLuint GLES2Implementation::CreateImageCHROMIUMHelper(
+    GLsizei width, GLsizei height, GLenum internalformat) {
+  if (width <= 0) {
+    SetGLError(GL_INVALID_VALUE, "glCreateImageCHROMIUM", "width <= 0");
+    return 0;
+  }
+
+  if (height <= 0) {
+    SetGLError(GL_INVALID_VALUE, "glCreateImageCHROMIUM", "height <= 0");
+    return 0;
+  }
+  // Flush the command stream to ensure ordering in case the newly
+  // returned image_id has recently been in use with a different buffer.
+  helper_->CommandBufferHelper::Flush();
+
+  // Create new buffer.
+  GLuint buffer_id = gpu_memory_buffer_tracker_->CreateBuffer(
+      width, height, internalformat);
+  if (buffer_id == 0) {
+    SetGLError(GL_OUT_OF_MEMORY, "glCreateImageCHROMIUM", "out of GPU memory.");
+    return 0;
+  }
+  return buffer_id;
+}
+
+GLuint GLES2Implementation::CreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glCreateImageCHROMIUM("
+      << width << ", "
+      << height << ", "
+      << GLES2Util::GetStringTextureInternalFormat(internalformat) << ")");
+  GLuint image_id = CreateImageCHROMIUMHelper(width, height, internalformat);
+  CheckGLError();
+  return image_id;
+}
+
+void GLES2Implementation::DestroyImageCHROMIUMHelper(GLuint image_id) {
+  GpuMemoryBuffer* gpu_buffer =
+       gpu_memory_buffer_tracker_->GetBuffer(image_id);
+   if (!gpu_buffer) {
+     SetGLError(GL_INVALID_OPERATION, "glDestroyImageCHROMIUM",
+                "invalid image");
+     return;
+   }
+
+   // Flush the command stream to make sure all pending commands
+   // that may refer to the image_id are executed on the service side.
+   helper_->CommandBufferHelper::Flush();
+   gpu_memory_buffer_tracker_->RemoveBuffer(image_id);
+}
+
+void GLES2Implementation::DestroyImageCHROMIUM(GLuint image_id) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDestroyImageCHROMIUM("
+      << image_id << ")");
+  DestroyImageCHROMIUMHelper(image_id);
+  CheckGLError();
+}
+
+void GLES2Implementation::UnmapImageCHROMIUMHelper(GLuint image_id) {
+   GpuMemoryBuffer* gpu_buffer =
+       gpu_memory_buffer_tracker_->GetBuffer(image_id);
+   if (!gpu_buffer) {
+     SetGLError(GL_INVALID_OPERATION, "glUnmapImageCHROMIUM", "invalid image");
+     return;
+   }
+
+   if (!gpu_buffer->IsMapped()) {
+     SetGLError(GL_INVALID_OPERATION, "glUnmapImageCHROMIUM", "not mapped");
+     return;
+   }
+   gpu_buffer->Unmap();
+}
+
+void GLES2Implementation::UnmapImageCHROMIUM(GLuint image_id) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glUnmapImageCHROMIUM("
+       << image_id << ")");
+
+  UnmapImageCHROMIUMHelper(image_id);
+  CheckGLError();
+}
+
+void* GLES2Implementation::MapImageCHROMIUMHelper(GLuint image_id,
+                                                  GLenum access) {
+   GpuMemoryBuffer* gpu_buffer =
+       gpu_memory_buffer_tracker_->GetBuffer(image_id);
+   if (!gpu_buffer) {
+     SetGLError(GL_INVALID_OPERATION, "glMapImageCHROMIUM", "invalid image");
+     return NULL;
+   }
+   GpuMemoryBuffer::AccessMode mode;
+   switch(access) {
+     case GL_WRITE_ONLY:
+       mode = GpuMemoryBuffer::WRITE_ONLY;
+       break;
+     case GL_READ_ONLY:
+       mode = GpuMemoryBuffer::READ_ONLY;
+       break;
+     case GL_READ_WRITE:
+       mode = GpuMemoryBuffer::READ_WRITE;
+       break;
+     default:
+       SetGLError(GL_INVALID_ENUM, "glMapImageCHROMIUM",
+                  "invalid GPU access mode");
+       return NULL;
+   }
+
+   if (gpu_buffer->IsMapped()) {
+     SetGLError(GL_INVALID_OPERATION, "glMapImageCHROMIUM", "already mapped");
+     return NULL;
+   }
+
+   void* mapped_buffer = NULL;
+   gpu_buffer->Map(mode, &mapped_buffer);
+   return mapped_buffer;
+}
+
+void* GLES2Implementation::MapImageCHROMIUM(
+    GLuint image_id, GLenum access) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapImageCHROMIUM("
+      << image_id << ", "
+      << GLES2Util::GetStringEnum(access) << ")");
+
+  void* mapped = MapImageCHROMIUMHelper(image_id, access);
+  CheckGLError();
+  return mapped;
+}
+
+void GLES2Implementation::GetImageParameterivCHROMIUMHelper(
+    GLuint image_id, GLenum pname, GLint* params) {
+  if (pname != GL_IMAGE_ROWBYTES_CHROMIUM) {
+    SetGLError(GL_INVALID_ENUM, "glGetImageParameterivCHROMIUM",
+               "invalid parameter");
+    return;
+  }
+
+  GpuMemoryBuffer* gpu_buffer =
+      gpu_memory_buffer_tracker_->GetBuffer(image_id);
+  if (!gpu_buffer) {
+    SetGLError(GL_INVALID_OPERATION, "glGetImageParameterivCHROMIUM",
+               "invalid image");
+    return;
+  }
+
+  *params = gpu_buffer->GetStride();
+}
+
+void GLES2Implementation::GetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_VALIDATE_DESTINATION_INITALIZATION(GLint, params);
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glMapImageCHROMIUM("
+      << image_id << ", "
+      << GLES2Util::GetStringBufferParameter(pname) << ", "
+      << static_cast<const void*>(params) << ")");
+  GetImageParameterivCHROMIUM(image_id, pname, params);
+  CheckGLError();
+}
+
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
 // instead of having to edit some template or the code generator.
-#include "../client/gles2_implementation_impl_autogen.h"
+#include "gpu/command_buffer/client/gles2_implementation_impl_autogen.h"
 
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h
index a633dd6..c693383 100644
--- a/gpu/command_buffer/client/gles2_implementation.h
+++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -14,18 +14,20 @@
 #include <vector>
 
 #include "base/memory/scoped_ptr.h"
-#include "../client/buffer_tracker.h"
-#include "../client/client_context_state.h"
-#include "../client/gles2_cmd_helper.h"
-#include "../client/gles2_interface.h"
-#include "../client/query_tracker.h"
-#include "../client/ref_counted.h"
-#include "../client/ring_buffer.h"
-#include "../client/share_group.h"
-#include "../common/compiler_specific.h"
-#include "../common/debug_marker_manager.h"
-#include "../common/gles2_cmd_utils.h"
 #include "gles2_impl_export.h"
+#include "gpu/command_buffer/client/buffer_tracker.h"
+#include "gpu/command_buffer/client/client_context_state.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_tracker.h"
+#include "gpu/command_buffer/client/image_factory.h"
+#include "gpu/command_buffer/client/query_tracker.h"
+#include "gpu/command_buffer/client/ref_counted.h"
+#include "gpu/command_buffer/client/ring_buffer.h"
+#include "gpu/command_buffer/client/share_group.h"
+#include "gpu/command_buffer/common/compiler_specific.h"
+#include "gpu/command_buffer/common/debug_marker_manager.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
 
 #if !defined(NDEBUG) && !defined(__native_client__) && !defined(GLES2_CONFORMANCE_TESTS)  // NOLINT
   #if defined(GLES2_INLINE_OPTIMIZATION)
@@ -102,6 +104,7 @@
 
 namespace gles2 {
 
+class ImageFactory;
 class VertexArrayObjectManager;
 
 // This class emulates GLES2 over command buffers. It can be used by a client
@@ -174,7 +177,8 @@
       ShareGroup* share_group,
       TransferBufferInterface* transfer_buffer,
       bool share_resources,
-      bool bind_generates_resource);
+      bool bind_generates_resource,
+      ImageFactory* image_factory);
 
   virtual ~GLES2Implementation();
 
@@ -193,7 +197,7 @@
   // Include the auto-generated part of this class. We split this because
   // it means we can easily edit the non-auto generated parts right here in
   // this file instead of having to edit some template or the code generator.
-  #include "../client/gles2_implementation_autogen.h"
+  #include "gpu/command_buffer/client/gles2_implementation_autogen.h"
 
   virtual void DisableVertexAttribArray(GLuint index) OVERRIDE;
   virtual void EnableVertexAttribArray(GLuint index) OVERRIDE;
@@ -496,6 +500,14 @@
       GLenum target, GLintptr offset, GLsizeiptr size, const void* data,
       ScopedTransferBufferPtr* buffer);
 
+  GLuint CreateImageCHROMIUMHelper(
+      GLsizei width, GLsizei height, GLenum internalformat);
+  void DestroyImageCHROMIUMHelper(GLuint image_id);
+  void* MapImageCHROMIUMHelper(GLuint image_id, GLenum access);
+  void UnmapImageCHROMIUMHelper(GLuint image_id);
+  void GetImageParameterivCHROMIUMHelper(
+      GLuint image_id, GLenum pname, GLint* params);
+
   // Helper for GetVertexAttrib
   bool GetVertexAttribHelper(GLuint index, GLenum pname, uint32* param);
 
@@ -654,10 +666,14 @@
 
   scoped_ptr<BufferTracker> buffer_tracker_;
 
+  scoped_ptr<GpuMemoryBufferTracker> gpu_memory_buffer_tracker_;
+
   ErrorMessageCallback* error_message_callback_;
 
   scoped_ptr<std::string> current_trace_name_;
 
+  ImageFactory* image_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(GLES2Implementation);
 };
 
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 3510bbf..b60730b 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -438,6 +438,10 @@
 
 virtual GLboolean UnmapBufferCHROMIUM(GLuint target) OVERRIDE;
 
+virtual void* MapImageCHROMIUM(GLuint image_id, GLenum access) OVERRIDE;
+
+virtual void UnmapImageCHROMIUM(GLuint image_id) OVERRIDE;
+
 virtual void* MapBufferSubDataCHROMIUM(
     GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) OVERRIDE;
 
@@ -449,7 +453,8 @@
 
 virtual void UnmapTexSubImage2DCHROMIUM(const void* mem) OVERRIDE;
 
-virtual void ResizeCHROMIUM(GLuint width, GLuint height) OVERRIDE;
+virtual void ResizeCHROMIUM(
+    GLuint width, GLuint height, GLfloat scale_factor) OVERRIDE;
 
 virtual const GLchar* GetRequestableExtensionsCHROMIUM() OVERRIDE;
 
@@ -468,6 +473,14 @@
 
 virtual void DestroyStreamTextureCHROMIUM(GLuint texture) OVERRIDE;
 
+virtual GLuint CreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat) OVERRIDE;
+
+virtual void DestroyImageCHROMIUM(GLuint image_id) OVERRIDE;
+
+virtual void GetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params) OVERRIDE;
+
 virtual void GetTranslatedShaderSourceANGLE(
     GLuint shader, GLsizei bufsize, GLsizei* length, char* source) OVERRIDE;
 
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index 4343ca8..7208425 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -401,7 +401,8 @@
           NULL,
           transfer_buffer_.get(),
           shared_resources,
-          bind_generates_resource));
+          bind_generates_resource,
+          NULL));
       ASSERT_TRUE(gl_->Initialize(
           kTransferBufferSize,
           kTransferBufferSize,
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
index d783468..a93f8eb 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -1643,9 +1643,9 @@
     cmds::ResizeCHROMIUM cmd;
   };
   Cmds expected;
-  expected.cmd.Init(1, 2);
+  expected.cmd.Init(1, 2, 3);
 
-  gl_->ResizeCHROMIUM(1, 2);
+  gl_->ResizeCHROMIUM(1, 2, 3);
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
 // TODO: Implement unit test for GetRequestableExtensionsCHROMIUM
diff --git a/gpu/command_buffer/client/gles2_interface.cc b/gpu/command_buffer/client/gles2_interface.cc
index b09e5f8..a94f167 100644
--- a/gpu/command_buffer/client/gles2_interface.cc
+++ b/gpu/command_buffer/client/gles2_interface.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/gles2_interface.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/gles2_interface.h b/gpu/command_buffer/client/gles2_interface.h
index 9fe54a1..d1659bf 100644
--- a/gpu/command_buffer/client/gles2_interface.h
+++ b/gpu/command_buffer/client/gles2_interface.h
@@ -20,7 +20,7 @@
   // Include the auto-generated part of this class. We split this because
   // it means we can easily edit the non-auto generated parts right here in
   // this file instead of having to edit some template or the code generator.
-  #include "../client/gles2_interface_autogen.h"
+  #include "gpu/command_buffer/client/gles2_interface_autogen.h"
 };
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 865f7ed..a933b81 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -251,6 +251,8 @@
 virtual GLboolean EnableFeatureCHROMIUM(const char* feature) = 0;
 virtual void* MapBufferCHROMIUM(GLuint target, GLenum access) = 0;
 virtual GLboolean UnmapBufferCHROMIUM(GLuint target) = 0;
+virtual void* MapImageCHROMIUM(GLuint image_id, GLenum access) = 0;
+virtual void UnmapImageCHROMIUM(GLuint image_id) = 0;
 virtual void* MapBufferSubDataCHROMIUM(
     GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) = 0;
 virtual void UnmapBufferSubDataCHROMIUM(const void* mem) = 0;
@@ -258,7 +260,8 @@
     GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
     GLsizei height, GLenum format, GLenum type, GLenum access) = 0;
 virtual void UnmapTexSubImage2DCHROMIUM(const void* mem) = 0;
-virtual void ResizeCHROMIUM(GLuint width, GLuint height) = 0;
+virtual void ResizeCHROMIUM(GLuint width, GLuint height, GLfloat scale_factor) =
+    0;
 virtual const GLchar* GetRequestableExtensionsCHROMIUM() = 0;
 virtual void RequestExtensionCHROMIUM(const char* extension) = 0;
 virtual void RateLimitOffscreenContextCHROMIUM() = 0;
@@ -268,6 +271,11 @@
     GLuint program, GLsizei bufsize, GLsizei* size, void* info) = 0;
 virtual GLuint CreateStreamTextureCHROMIUM(GLuint texture) = 0;
 virtual void DestroyStreamTextureCHROMIUM(GLuint texture) = 0;
+virtual GLuint CreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat) = 0;
+virtual void DestroyImageCHROMIUM(GLuint image_id) = 0;
+virtual void GetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params) = 0;
 virtual void GetTranslatedShaderSourceANGLE(
     GLuint shader, GLsizei bufsize, GLsizei* length, char* source) = 0;
 virtual void PostSubBufferCHROMIUM(
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index 769b260..a16fe5d 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -279,6 +279,8 @@
 virtual GLboolean EnableFeatureCHROMIUM(const char* feature) OVERRIDE;
 virtual void* MapBufferCHROMIUM(GLuint target, GLenum access) OVERRIDE;
 virtual GLboolean UnmapBufferCHROMIUM(GLuint target) OVERRIDE;
+virtual void* MapImageCHROMIUM(GLuint image_id, GLenum access) OVERRIDE;
+virtual void UnmapImageCHROMIUM(GLuint image_id) OVERRIDE;
 virtual void* MapBufferSubDataCHROMIUM(
     GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) OVERRIDE;
 virtual void UnmapBufferSubDataCHROMIUM(const void* mem) OVERRIDE;
@@ -286,7 +288,8 @@
     GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
     GLsizei height, GLenum format, GLenum type, GLenum access) OVERRIDE;
 virtual void UnmapTexSubImage2DCHROMIUM(const void* mem) OVERRIDE;
-virtual void ResizeCHROMIUM(GLuint width, GLuint height) OVERRIDE;
+virtual void ResizeCHROMIUM(
+    GLuint width, GLuint height, GLfloat scale_factor) OVERRIDE;
 virtual const GLchar* GetRequestableExtensionsCHROMIUM() OVERRIDE;
 virtual void RequestExtensionCHROMIUM(const char* extension) OVERRIDE;
 virtual void RateLimitOffscreenContextCHROMIUM() OVERRIDE;
@@ -297,6 +300,11 @@
     GLuint program, GLsizei bufsize, GLsizei* size, void* info) OVERRIDE;
 virtual GLuint CreateStreamTextureCHROMIUM(GLuint texture) OVERRIDE;
 virtual void DestroyStreamTextureCHROMIUM(GLuint texture) OVERRIDE;
+virtual GLuint CreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat) OVERRIDE;
+virtual void DestroyImageCHROMIUM(GLuint image_id) OVERRIDE;
+virtual void GetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params) OVERRIDE;
 virtual void GetTranslatedShaderSourceANGLE(
     GLuint shader, GLsizei bufsize, GLsizei* length, char* source) OVERRIDE;
 virtual void PostSubBufferCHROMIUM(
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index b1c4ac5..ce4b53b 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -527,6 +527,12 @@
 GLboolean GLES2InterfaceStub::UnmapBufferCHROMIUM(GLuint /* target */) {
   return 0;
 }
+void* GLES2InterfaceStub::MapImageCHROMIUM(
+    GLuint /* image_id */, GLenum /* access */) {
+  return 0;
+}
+void GLES2InterfaceStub::UnmapImageCHROMIUM(GLuint /* image_id */) {
+}
 void* GLES2InterfaceStub::MapBufferSubDataCHROMIUM(
     GLuint /* target */, GLintptr /* offset */, GLsizeiptr /* size */,
     GLenum /* access */) {
@@ -543,7 +549,7 @@
 void GLES2InterfaceStub::UnmapTexSubImage2DCHROMIUM(const void* /* mem */) {
 }
 void GLES2InterfaceStub::ResizeCHROMIUM(
-    GLuint /* width */, GLuint /* height */) {
+    GLuint /* width */, GLuint /* height */, GLfloat /* scale_factor */) {
 }
 const GLchar* GLES2InterfaceStub::GetRequestableExtensionsCHROMIUM() {
   return 0;
@@ -566,6 +572,15 @@
 }
 void GLES2InterfaceStub::DestroyStreamTextureCHROMIUM(GLuint /* texture */) {
 }
+GLuint GLES2InterfaceStub::CreateImageCHROMIUM(
+    GLsizei /* width */, GLsizei /* height */, GLenum /* internalformat */) {
+  return 0;
+}
+void GLES2InterfaceStub::DestroyImageCHROMIUM(GLuint /* image_id */) {
+}
+void GLES2InterfaceStub::GetImageParameterivCHROMIUM(
+    GLuint /* image_id */, GLenum /* pname */, GLint* /* params */) {
+}
 void GLES2InterfaceStub::GetTranslatedShaderSourceANGLE(
     GLuint /* shader */, GLsizei /* bufsize */, GLsizei* /* length */,
     char* /* source */) {
diff --git a/gpu/command_buffer/client/gles2_lib.cc b/gpu/command_buffer/client/gles2_lib.cc
index e7b11e8..6c8b0ea 100644
--- a/gpu/command_buffer/client/gles2_lib.cc
+++ b/gpu/command_buffer/client/gles2_lib.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/gles2_lib.h"
+#include "gpu/command_buffer/client/gles2_lib.h"
 #include <string.h>
-#include "../common/thread_local.h"
+#include "gpu/command_buffer/common/thread_local.h"
 
 namespace gles2 {
 
diff --git a/gpu/command_buffer/client/gles2_lib.h b/gpu/command_buffer/client/gles2_lib.h
index 2cad3f1..b90a2d8 100644
--- a/gpu/command_buffer/client/gles2_lib.h
+++ b/gpu/command_buffer/client/gles2_lib.h
@@ -7,8 +7,8 @@
 #ifndef GPU_COMMAND_BUFFER_CLIENT_GLES2_LIB_H_
 #define GPU_COMMAND_BUFFER_CLIENT_GLES2_LIB_H_
 
-#include "../client/gles2_interface.h"
 #include "gpu/command_buffer/client/gles2_c_lib_export.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
 
 namespace gles2 {
 
diff --git a/gpu/command_buffer/client/gles2_trace_implementation.cc b/gpu/command_buffer/client/gles2_trace_implementation.cc
index 2497342..234f243 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation.cc
+++ b/gpu/command_buffer/client/gles2_trace_implementation.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/gles2_trace_implementation.h"
-#include "../common/trace_event.h"
+#include "gpu/command_buffer/client/gles2_trace_implementation.h"
+#include "gpu/command_buffer/common/trace_event.h"
 
 namespace gpu {
 namespace gles2 {
@@ -18,7 +18,7 @@
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
 // instead of having to edit some template or the code generator.
-#include "../client/gles2_trace_implementation_impl_autogen.h"
+#include "gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h"
 
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/gles2_trace_implementation.h b/gpu/command_buffer/client/gles2_trace_implementation.h
index 0925e22..18e6a79 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation.h
@@ -5,9 +5,9 @@
 #ifndef GPU_COMMAND_BUFFER_CLIENT_GLES2_TRACE_IMPLEMENTATION_H_
 #define GPU_COMMAND_BUFFER_CLIENT_GLES2_TRACE_IMPLEMENTATION_H_
 
-#include "../client/gles2_interface.h"
-#include "../common/compiler_specific.h"
 #include "gles2_impl_export.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/compiler_specific.h"
 
 namespace gpu {
 namespace gles2 {
@@ -21,7 +21,7 @@
   // Include the auto-generated part of this class. We split this because
   // it means we can easily edit the non-auto generated parts right here in
   // this file instead of having to edit some template or the code generator.
-  #include "../client/gles2_trace_implementation_autogen.h"
+  #include "gpu/command_buffer/client/gles2_trace_implementation_autogen.h"
 
  private:
   GLES2Interface* gl_;
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 7ec9eb6..bef6ce8 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -279,6 +279,8 @@
 virtual GLboolean EnableFeatureCHROMIUM(const char* feature) OVERRIDE;
 virtual void* MapBufferCHROMIUM(GLuint target, GLenum access) OVERRIDE;
 virtual GLboolean UnmapBufferCHROMIUM(GLuint target) OVERRIDE;
+virtual void* MapImageCHROMIUM(GLuint image_id, GLenum access) OVERRIDE;
+virtual void UnmapImageCHROMIUM(GLuint image_id) OVERRIDE;
 virtual void* MapBufferSubDataCHROMIUM(
     GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) OVERRIDE;
 virtual void UnmapBufferSubDataCHROMIUM(const void* mem) OVERRIDE;
@@ -286,7 +288,8 @@
     GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
     GLsizei height, GLenum format, GLenum type, GLenum access) OVERRIDE;
 virtual void UnmapTexSubImage2DCHROMIUM(const void* mem) OVERRIDE;
-virtual void ResizeCHROMIUM(GLuint width, GLuint height) OVERRIDE;
+virtual void ResizeCHROMIUM(
+    GLuint width, GLuint height, GLfloat scale_factor) OVERRIDE;
 virtual const GLchar* GetRequestableExtensionsCHROMIUM() OVERRIDE;
 virtual void RequestExtensionCHROMIUM(const char* extension) OVERRIDE;
 virtual void RateLimitOffscreenContextCHROMIUM() OVERRIDE;
@@ -297,6 +300,11 @@
     GLuint program, GLsizei bufsize, GLsizei* size, void* info) OVERRIDE;
 virtual GLuint CreateStreamTextureCHROMIUM(GLuint texture) OVERRIDE;
 virtual void DestroyStreamTextureCHROMIUM(GLuint texture) OVERRIDE;
+virtual GLuint CreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat) OVERRIDE;
+virtual void DestroyImageCHROMIUM(GLuint image_id) OVERRIDE;
+virtual void GetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params) OVERRIDE;
 virtual void GetTranslatedShaderSourceANGLE(
     GLuint shader, GLsizei bufsize, GLsizei* length, char* source) OVERRIDE;
 virtual void PostSubBufferCHROMIUM(
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 0471880..a99a36a 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -978,6 +978,17 @@
   return gl_->UnmapBufferCHROMIUM(target);
 }
 
+void* GLES2TraceImplementation::MapImageCHROMIUM(
+    GLuint image_id, GLenum access) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::MapImageCHROMIUM");
+  return gl_->MapImageCHROMIUM(image_id, access);
+}
+
+void GLES2TraceImplementation::UnmapImageCHROMIUM(GLuint image_id) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::UnmapImageCHROMIUM");
+  gl_->UnmapImageCHROMIUM(image_id);
+}
+
 void* GLES2TraceImplementation::MapBufferSubDataCHROMIUM(
     GLuint target, GLintptr offset, GLsizeiptr size, GLenum access) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::MapBufferSubDataCHROMIUM");
@@ -1002,9 +1013,10 @@
   gl_->UnmapTexSubImage2DCHROMIUM(mem);
 }
 
-void GLES2TraceImplementation::ResizeCHROMIUM(GLuint width, GLuint height) {
+void GLES2TraceImplementation::ResizeCHROMIUM(
+    GLuint width, GLuint height, GLfloat scale_factor) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::ResizeCHROMIUM");
-  gl_->ResizeCHROMIUM(width, height);
+  gl_->ResizeCHROMIUM(width, height, scale_factor);
 }
 
 const GLchar* GLES2TraceImplementation::GetRequestableExtensionsCHROMIUM() {
@@ -1045,6 +1057,23 @@
   gl_->DestroyStreamTextureCHROMIUM(texture);
 }
 
+GLuint GLES2TraceImplementation::CreateImageCHROMIUM(
+    GLsizei width, GLsizei height, GLenum internalformat) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::CreateImageCHROMIUM");
+  return gl_->CreateImageCHROMIUM(width, height, internalformat);
+}
+
+void GLES2TraceImplementation::DestroyImageCHROMIUM(GLuint image_id) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::DestroyImageCHROMIUM");
+  gl_->DestroyImageCHROMIUM(image_id);
+}
+
+void GLES2TraceImplementation::GetImageParameterivCHROMIUM(
+    GLuint image_id, GLenum pname, GLint* params) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::GetImageParameterivCHROMIUM");  // NOLINT
+  gl_->GetImageParameterivCHROMIUM(image_id, pname, params);
+}
+
 void GLES2TraceImplementation::GetTranslatedShaderSourceANGLE(
     GLuint shader, GLsizei bufsize, GLsizei* length, char* source) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::GetTranslatedShaderSourceANGLE");  // NOLINT
diff --git a/gpu/command_buffer/client/gpu_memory_buffer.h b/gpu/command_buffer/client/gpu_memory_buffer.h
index 14e95ee..480eb89 100644
--- a/gpu/command_buffer/client/gpu_memory_buffer.h
+++ b/gpu/command_buffer/client/gpu_memory_buffer.h
@@ -6,7 +6,6 @@
 #define GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_H_
 
 #include "base/basictypes.h"
-#include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
 #include "gles2_impl_export.h"
 
@@ -23,11 +22,10 @@
 // behavior and is not allowed.
 class GLES2_IMPL_EXPORT GpuMemoryBuffer {
  public:
-  typedef base::Callback<scoped_ptr<GpuMemoryBuffer>(int, int)> Creator;
   enum AccessMode {
     READ_ONLY,
     WRITE_ONLY,
-    READ_OR_WRITE,
+    READ_WRITE,
   };
 
   // Frees a previously allocated buffer. Freeing a buffer that is still
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_factory.cc b/gpu/command_buffer/client/gpu_memory_buffer_factory.cc
deleted file mode 100644
index c0e5b9f..0000000
--- a/gpu/command_buffer/client/gpu_memory_buffer_factory.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2013 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 "gpu/command_buffer/client/gpu_memory_buffer_factory.h"
-
-#include "base/logging.h"
-
-namespace gpu {
-
-namespace {
-GpuMemoryBuffer::Creator* g_gpu_memory_buffer_factory_ = NULL;
-}
-
-const GpuMemoryBuffer::Creator& GetProcessDefaultGpuMemoryBufferFactory() {
-  return *g_gpu_memory_buffer_factory_;
-}
-
-void SetProcessDefaultGpuMemoryBufferFactory(
-    const GpuMemoryBuffer::Creator& factory) {
-  DCHECK(g_gpu_memory_buffer_factory_ == NULL);
-  g_gpu_memory_buffer_factory_ = new GpuMemoryBuffer::Creator(factory);
-}
-
-}  // namespace gpu
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_factory.h b/gpu/command_buffer/client/gpu_memory_buffer_factory.h
deleted file mode 100644
index dd10a7d..0000000
--- a/gpu/command_buffer/client/gpu_memory_buffer_factory.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2013 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 GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_FACTORY_H_
-#define GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_FACTORY_H_
-
-#include "gles2_impl_export.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer.h"
-
-namespace gpu {
-
-// Getter and setter for a GpuMemoryBuffer factory for the current process.
-// Currently it is only used for Android Webview where both browser and
-// renderer are within the same process.
-
-// It is not valid to call this method before the setter is called.
-GLES2_IMPL_EXPORT const GpuMemoryBuffer::Creator&
-    GetProcessDefaultGpuMemoryBufferFactory();
-
-// It is illegal to call the setter more than once.
-GLES2_IMPL_EXPORT void SetProcessDefaultGpuMemoryBufferFactory(
-    const GpuMemoryBuffer::Creator& factory);
-
-}  // namespace gpu
-
-#endif  // GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_FACTORY_H_
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_mock.cc b/gpu/command_buffer/client/gpu_memory_buffer_mock.cc
new file mode 100644
index 0000000..289b0fe
--- /dev/null
+++ b/gpu/command_buffer/client/gpu_memory_buffer_mock.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2013 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 "gpu/command_buffer/client/gpu_memory_buffer_mock.h"
+
+namespace gpu {
+
+GpuMemoryBufferMock::GpuMemoryBufferMock(int width, int height) {
+}
+
+GpuMemoryBufferMock::~GpuMemoryBufferMock() {
+  Die();
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_mock.h b/gpu/command_buffer/client/gpu_memory_buffer_mock.h
new file mode 100644
index 0000000..6a4db89
--- /dev/null
+++ b/gpu/command_buffer/client/gpu_memory_buffer_mock.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 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 GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_MOCK_H_
+#define GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_MOCK_H_
+
+#include "gpu/command_buffer/client/gpu_memory_buffer.h"
+#include "base/basictypes.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace gpu {
+
+class GpuMemoryBufferMock : public GpuMemoryBuffer {
+ public:
+  GpuMemoryBufferMock(int width, int height);
+  virtual ~GpuMemoryBufferMock();
+
+  MOCK_METHOD2(Map, void(GpuMemoryBuffer::AccessMode, void**));
+  MOCK_METHOD0(Unmap, void());
+  MOCK_METHOD0(IsMapped, bool());
+  MOCK_METHOD0(GetNativeBuffer, void*());
+  MOCK_METHOD0(GetStride, uint32());
+  MOCK_METHOD0(Die, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferMock);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_MOCK_H_
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_tracker.cc b/gpu/command_buffer/client/gpu_memory_buffer_tracker.cc
new file mode 100644
index 0000000..894898b
--- /dev/null
+++ b/gpu/command_buffer/client/gpu_memory_buffer_tracker.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2013 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 "gpu/command_buffer/client/gpu_memory_buffer_tracker.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer.h"
+#include "gpu/command_buffer/client/image_factory.h"
+
+namespace gpu {
+namespace gles2 {
+
+GpuMemoryBufferTracker::GpuMemoryBufferTracker(ImageFactory* factory)
+    : buffers_(),
+      factory_(factory) {
+}
+
+GpuMemoryBufferTracker::~GpuMemoryBufferTracker() {
+  while (!buffers_.empty()) {
+    RemoveBuffer(buffers_.begin()->first);
+  }
+}
+
+GLuint GpuMemoryBufferTracker::CreateBuffer(
+    GLsizei width, GLsizei height, GLenum internalformat) {
+  GLuint image_id = 0;
+  DCHECK(factory_);
+  scoped_ptr<GpuMemoryBuffer> buffer =
+      factory_->CreateGpuMemoryBuffer(width, height, internalformat, &image_id);
+
+  if (buffer.get() == NULL)
+    return 0;
+
+  std::pair<BufferMap::iterator, bool> result =
+      buffers_.insert(std::make_pair(image_id, buffer.release()));
+  GPU_DCHECK(result.second);
+
+  return image_id;
+}
+
+GpuMemoryBuffer* GpuMemoryBufferTracker::GetBuffer(GLuint image_id) {
+  BufferMap::iterator it = buffers_.find(image_id);
+  return (it != buffers_.end()) ? it->second : NULL;
+}
+
+void GpuMemoryBufferTracker::RemoveBuffer(GLuint image_id) {
+  BufferMap::iterator buffer_it = buffers_.find(image_id);
+  if (buffer_it != buffers_.end()) {
+    GpuMemoryBuffer* buffer = buffer_it->second;
+    buffers_.erase(buffer_it);
+    delete buffer;
+  }
+  DCHECK(factory_);
+  factory_->DeleteGpuMemoryBuffer(image_id);
+}
+
+}  // namespace gles2
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_tracker.h b/gpu/command_buffer/client/gpu_memory_buffer_tracker.h
new file mode 100644
index 0000000..f2f6e9f
--- /dev/null
+++ b/gpu/command_buffer/client/gpu_memory_buffer_tracker.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 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 GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_TRACKER_H_
+#define GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_TRACKER_H_
+
+#include <GLES2/gl2.h>
+
+#include "base/basictypes.h"
+#include "gles2_impl_export.h"
+#include "gpu/command_buffer/client/hash_tables.h"
+
+namespace gpu {
+class GpuMemoryBuffer;
+
+namespace gles2 {
+class ImageFactory;
+
+// Tracks GPU memory buffer objects on the client side.
+class GLES2_IMPL_EXPORT GpuMemoryBufferTracker {
+ public:
+  // Ownership of |factory| remains with caller.
+  explicit GpuMemoryBufferTracker(ImageFactory* factory);
+  virtual ~GpuMemoryBufferTracker();
+
+  GLuint CreateBuffer(
+      GLsizei width, GLsizei height, GLenum internalformat);
+  GpuMemoryBuffer* GetBuffer(GLuint image_id);
+  void RemoveBuffer(GLuint image_id);
+
+ private:
+  typedef gpu::hash_map<GLuint, GpuMemoryBuffer*> BufferMap;
+  BufferMap buffers_;
+  ImageFactory* factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferTracker);
+};
+
+}  // namespace gles2
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_CLIENT_GPU_MEMORY_BUFFER_TRACKER_H_
diff --git a/gpu/command_buffer/client/image_factory.h b/gpu/command_buffer/client/image_factory.h
new file mode 100644
index 0000000..b752cb1
--- /dev/null
+++ b/gpu/command_buffer/client/image_factory.h
@@ -0,0 +1,33 @@
+// Copyright 2013 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 GPU_COMMAND_BUFFER_CLIENT_IMAGE_FACTORY_H_
+#define GPU_COMMAND_BUFFER_CLIENT_IMAGE_FACTORY_H_
+
+#include <GLES2/gl2.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "gles2_impl_export.h"
+
+namespace gpu {
+class GpuMemoryBuffer;
+
+namespace gles2 {
+
+class GLES2_IMPL_EXPORT ImageFactory {
+
+ public:
+  virtual ~ImageFactory() {}
+
+  // Create a GpuMemoryBuffer and makes it available to the
+  // service side by inserting it to the ImageManager.
+  virtual scoped_ptr<GpuMemoryBuffer> CreateGpuMemoryBuffer(
+      int width, int height, GLenum internalformat, unsigned* image_id) = 0;
+  virtual void DeleteGpuMemoryBuffer(unsigned image_id) = 0;
+};
+
+}  // namespace gles2
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_CLIENT_IMAGE_FACTORY_H_
diff --git a/gpu/command_buffer/client/image_factory_mock.cc b/gpu/command_buffer/client/image_factory_mock.cc
new file mode 100644
index 0000000..d167cee
--- /dev/null
+++ b/gpu/command_buffer/client/image_factory_mock.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2013 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 "gpu/command_buffer/client/image_factory_mock.h"
+
+namespace gpu {
+namespace gles2 {
+
+ImageFactoryMock::ImageFactoryMock(ImageManager* manager) {
+}
+
+ImageFactoryMock::~ImageFactoryMock() {
+}
+
+}  // namespace gles2
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/image_factory_mock.h b/gpu/command_buffer/client/image_factory_mock.h
new file mode 100644
index 0000000..2516d69
--- /dev/null
+++ b/gpu/command_buffer/client/image_factory_mock.h
@@ -0,0 +1,41 @@
+// Copyright 2013 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 GPU_COMMAND_BUFFER_CLIENT_IMAGE_FACTORY_MOCK_H_
+#define GPU_COMMAND_BUFFER_CLIENT_IMAGE_FACTORY_MOCK_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer.h"
+#include "gpu/command_buffer/client/image_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace gpu {
+namespace gles2 {
+class ImageManager;
+
+// Mock implementation of ImageFactory
+class ImageFactoryMock : public ImageFactory {
+ public:
+  ImageFactoryMock(ImageManager* image_manager);
+  virtual ~ImageFactoryMock();
+
+  MOCK_METHOD4(CreateGpuMemoryBufferMock, GpuMemoryBuffer*(
+      int width, int height, GLenum internalformat, unsigned* image_id));
+  MOCK_METHOD1(DeleteGpuMemoryBuffer, void(unsigned));
+  // Workaround for mocking methods that return scoped_ptrs
+  virtual scoped_ptr<GpuMemoryBuffer> CreateGpuMemoryBuffer(
+      int width, int height, GLenum internalformat,
+      unsigned* image_id) OVERRIDE {
+    return scoped_ptr<GpuMemoryBuffer>(CreateGpuMemoryBufferMock(
+        width, height, internalformat, image_id));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ImageFactoryMock);
+};
+
+}  // namespace gles2
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_CLIENT_IMAGE_FACTORY_MOCK_H_
diff --git a/gpu/command_buffer/client/mapped_memory.cc b/gpu/command_buffer/client/mapped_memory.cc
index 8e51801..34a6c5c 100644
--- a/gpu/command_buffer/client/mapped_memory.cc
+++ b/gpu/command_buffer/client/mapped_memory.cc
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "gpu/command_buffer/client/mapped_memory.h"
+
 #include <algorithm>
 #include <functional>
 
-#include "../client/mapped_memory.h"
-#include "../client/cmd_buffer_helper.h"
+#include "gpu/command_buffer/client/cmd_buffer_helper.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/mapped_memory.h b/gpu/command_buffer/client/mapped_memory.h
index 93390a7..2dfef72 100644
--- a/gpu/command_buffer/client/mapped_memory.h
+++ b/gpu/command_buffer/client/mapped_memory.h
@@ -7,10 +7,10 @@
 
 #include <vector>
 
-#include "../../gpu_export.h"
-#include "../common/types.h"
-#include "../client/fenced_allocator.h"
-#include "../common/buffer.h"
+#include "gpu/command_buffer/client/fenced_allocator.h"
+#include "gpu/command_buffer/common/buffer.h"
+#include "gpu/command_buffer/common/types.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/program_info_manager.cc b/gpu/command_buffer/client/program_info_manager.cc
index 71014ca..fb0c1f3 100644
--- a/gpu/command_buffer/client/program_info_manager.cc
+++ b/gpu/command_buffer/client/program_info_manager.cc
@@ -5,10 +5,10 @@
 #include <map>
 
 #include "base/compiler_specific.h"
-#include "../client/program_info_manager.h"
-#include "../client/atomicops.h"
-#include "../client/gles2_implementation.h"
-#include "../common/gles2_cmd_utils.h"
+#include "gpu/command_buffer/client/atomicops.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/program_info_manager.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc
index 5356ef5..76ebf72 100644
--- a/gpu/command_buffer/client/query_tracker.cc
+++ b/gpu/command_buffer/client/query_tracker.cc
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "gpu/command_buffer/client/query_tracker.h"
+
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <GLES2/gl2extchromium.h>
 
-#include "../client/query_tracker.h"
-
-#include "../client/atomicops.h"
-#include "../client/gles2_cmd_helper.h"
-#include "../client/gles2_implementation.h"
-#include "../client/mapped_memory.h"
-#include "../common/time.h"
+#include "gpu/command_buffer/client/atomicops.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/mapped_memory.h"
+#include "gpu/command_buffer/common/time.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h
index 5341ae9..144ae92 100644
--- a/gpu/command_buffer/client/query_tracker.h
+++ b/gpu/command_buffer/client/query_tracker.h
@@ -8,9 +8,9 @@
 #include <GLES2/gl2.h>
 
 #include <deque>
-#include "../client/hash_tables.h"
-#include "../common/gles2_cmd_format.h"
 #include "gles2_impl_export.h"
+#include "gpu/command_buffer/client/hash_tables.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/ring_buffer.cc b/gpu/command_buffer/client/ring_buffer.cc
index 762f834..46a86a2 100644
--- a/gpu/command_buffer/client/ring_buffer.cc
+++ b/gpu/command_buffer/client/ring_buffer.cc
@@ -4,9 +4,9 @@
 
 // This file contains the implementation of the RingBuffer class.
 
-#include "../client/ring_buffer.h"
+#include "gpu/command_buffer/client/ring_buffer.h"
 #include <algorithm>
-#include "../client/cmd_buffer_helper.h"
+#include "gpu/command_buffer/client/cmd_buffer_helper.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/ring_buffer.h b/gpu/command_buffer/client/ring_buffer.h
index c08cccd..d1f70a2 100644
--- a/gpu/command_buffer/client/ring_buffer.h
+++ b/gpu/command_buffer/client/ring_buffer.h
@@ -9,9 +9,9 @@
 
 #include <deque>
 
-#include "../../gpu_export.h"
-#include "../common/logging.h"
-#include "../common/types.h"
+#include "gpu/command_buffer/common/logging.h"
+#include "gpu/command_buffer/common/types.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 class CommandBufferHelper;
diff --git a/gpu/command_buffer/client/share_group.cc b/gpu/command_buffer/client/share_group.cc
index 041d79d..7ac53f0 100644
--- a/gpu/command_buffer/client/share_group.cc
+++ b/gpu/command_buffer/client/share_group.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/atomicops.h"
-#include "../client/share_group.h"
-#include "../client/gles2_implementation.h"
-#include "../client/program_info_manager.h"
-#include "../common/id_allocator.h"
-#include "../common/logging.h"
+#include "gpu/command_buffer/client/atomicops.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/program_info_manager.h"
+#include "gpu/command_buffer/client/share_group.h"
+#include "gpu/command_buffer/common/id_allocator.h"
+#include "gpu/command_buffer/common/logging.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/share_group.h b/gpu/command_buffer/client/share_group.h
index c9e3469..15d141c 100644
--- a/gpu/command_buffer/client/share_group.h
+++ b/gpu/command_buffer/client/share_group.h
@@ -7,9 +7,9 @@
 
 #include <GLES2/gl2.h>
 #include "base/memory/scoped_ptr.h"
-#include "../client/ref_counted.h"
-#include "../common/gles2_cmd_format.h"
 #include "gles2_impl_export.h"
+#include "gpu/command_buffer/client/ref_counted.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/client/transfer_buffer.cc b/gpu/command_buffer/client/transfer_buffer.cc
index 8348622..f4fe66c 100644
--- a/gpu/command_buffer/client/transfer_buffer.cc
+++ b/gpu/command_buffer/client/transfer_buffer.cc
@@ -4,8 +4,8 @@
 
 // A class to Manage a growing transfer buffer.
 
-#include "../client/transfer_buffer.h"
-#include "../client/cmd_buffer_helper.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/command_buffer/client/cmd_buffer_helper.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/transfer_buffer.h b/gpu/command_buffer/client/transfer_buffer.h
index 71c09bf..273f017 100644
--- a/gpu/command_buffer/client/transfer_buffer.h
+++ b/gpu/command_buffer/client/transfer_buffer.h
@@ -6,11 +6,11 @@
 #define GPU_COMMAND_BUFFER_CLIENT_TRANSFER_BUFFER_H_
 
 #include "base/memory/scoped_ptr.h"
-#include "../../gpu_export.h"
-#include "../common/buffer.h"
-#include "../common/compiler_specific.h"
-#include "../common/gles2_cmd_utils.h"
-#include "../client/ring_buffer.h"
+#include "gpu/command_buffer/client/ring_buffer.h"
+#include "gpu/command_buffer/common/buffer.h"
+#include "gpu/command_buffer/common/compiler_specific.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/client/vertex_array_object_manager.cc b/gpu/command_buffer/client/vertex_array_object_manager.cc
index 4209d06..f2a156e 100644
--- a/gpu/command_buffer/client/vertex_array_object_manager.cc
+++ b/gpu/command_buffer/client/vertex_array_object_manager.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../client/vertex_array_object_manager.h"
+#include "gpu/command_buffer/client/vertex_array_object_manager.h"
 
-#include "../client/gles2_cmd_helper.h"
-#include "../client/gles2_implementation.h"
-#include "../common/logging.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/common/logging.h"
 
 #if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
 #define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
diff --git a/gpu/command_buffer/client/vertex_array_object_manager.h b/gpu/command_buffer/client/vertex_array_object_manager.h
index dff2131..254b15d 100644
--- a/gpu/command_buffer/client/vertex_array_object_manager.h
+++ b/gpu/command_buffer/client/vertex_array_object_manager.h
@@ -7,9 +7,9 @@
 
 #include <GLES2/gl2.h>
 #include "base/memory/scoped_ptr.h"
-#include "../client/hash_tables.h"
-#include "../common/types.h"
 #include "gles2_impl_export.h"
+#include "gpu/command_buffer/client/hash_tables.h"
+#include "gpu/command_buffer/common/types.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt
index 1a9268b..ef56e5a 100644
--- a/gpu/command_buffer/cmd_buffer_functions.txt
+++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -175,12 +175,14 @@
 GL_APICALL GLboolean    GL_APIENTRY glEnableFeatureCHROMIUM (const char* feature);
 GL_APICALL void*        GL_APIENTRY glMapBufferCHROMIUM (GLuint target, GLenum access);
 GL_APICALL GLboolean    GL_APIENTRY glUnmapBufferCHROMIUM (GLuint target);
+GL_APICALL void*        GL_APIENTRY glMapImageCHROMIUM (GLuint image_id, GLenum access);
+GL_APICALL void         GL_APIENTRY glUnmapImageCHROMIUM (GLuint image_id);
 
 GL_APICALL void*        GL_APIENTRY glMapBufferSubDataCHROMIUM (GLuint target, GLintptrNotNegative offset, GLsizeiptr size, GLenum access);
 GL_APICALL void         GL_APIENTRY glUnmapBufferSubDataCHROMIUM (const void* mem);
 GL_APICALL void*        GL_APIENTRY glMapTexSubImage2DCHROMIUM (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLenum access);
 GL_APICALL void         GL_APIENTRY glUnmapTexSubImage2DCHROMIUM (const void* mem);
-GL_APICALL void         GL_APIENTRY glResizeCHROMIUM (GLuint width, GLuint height);
+GL_APICALL void         GL_APIENTRY glResizeCHROMIUM (GLuint width, GLuint height, GLfloat scale_factor);
 GL_APICALL const GLchar* GL_APIENTRY glGetRequestableExtensionsCHROMIUM (void);
 GL_APICALL void         GL_APIENTRY glRequestExtensionCHROMIUM (const char* extension);
 GL_APICALL void         GL_APIENTRY glRateLimitOffscreenContextCHROMIUM (void);
@@ -188,6 +190,9 @@
 GL_APICALL void         GL_APIENTRY glGetProgramInfoCHROMIUM (GLidProgram program, GLsizeiNotNegative bufsize, GLsizei* size, void* info);
 GL_APICALL GLuint       GL_APIENTRY glCreateStreamTextureCHROMIUM (GLuint texture);
 GL_APICALL void         GL_APIENTRY glDestroyStreamTextureCHROMIUM (GLuint texture);
+GL_APICALL GLuint       GL_APIENTRY glCreateImageCHROMIUM (GLsizei width, GLsizei height, GLenum internalformat);
+GL_APICALL void         GL_APIENTRY glDestroyImageCHROMIUM (GLuint image_id);
+GL_APICALL void         GL_APIENTRY glGetImageParameterivCHROMIUM (GLuint image_id, GLenum pname, GLint* params);
 GL_APICALL void         GL_APIENTRY glGetTranslatedShaderSourceANGLE (GLidShader shader, GLsizeiNotNegative bufsize, GLsizeiOptional* length, char* source);
 GL_APICALL void         GL_APIENTRY glPostSubBufferCHROMIUM (GLint x, GLint y, GLint width, GLint height);
 GL_APICALL void         GL_APIENTRY glTexImageIOSurface2DCHROMIUM (GLenumTextureBindTarget target, GLsizei width, GLsizei height, GLuint ioSurfaceId, GLuint plane);
diff --git a/gpu/command_buffer/common/buffer.h b/gpu/command_buffer/common/buffer.h
index 730053a..a2721a8 100644
--- a/gpu/command_buffer/common/buffer.h
+++ b/gpu/command_buffer/common/buffer.h
@@ -5,7 +5,7 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_BUFFER_H_
 #define GPU_COMMAND_BUFFER_COMMON_BUFFER_H_
 
-#include "../common/types.h"
+#include "gpu/command_buffer/common/types.h"
 
 namespace base {
   class SharedMemory;
diff --git a/gpu/command_buffer/common/cmd_buffer_common.cc b/gpu/command_buffer/common/cmd_buffer_common.cc
index 235cc5d..bfa625c 100644
--- a/gpu/command_buffer/common/cmd_buffer_common.cc
+++ b/gpu/command_buffer/common/cmd_buffer_common.cc
@@ -5,9 +5,9 @@
 // This file contains the binary format definition of the command buffer and
 // command buffer commands.
 
-#include "../common/cmd_buffer_common.h"
-#include "../common/command_buffer.h"
-#include "../common/logging.h"
+#include "gpu/command_buffer/common/cmd_buffer_common.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/logging.h"
 
 namespace gpu {
 #if !defined(_WIN32)
diff --git a/gpu/command_buffer/common/cmd_buffer_common.h b/gpu/command_buffer/common/cmd_buffer_common.h
index 5a87c40..0908198 100644
--- a/gpu/command_buffer/common/cmd_buffer_common.h
+++ b/gpu/command_buffer/common/cmd_buffer_common.h
@@ -9,10 +9,10 @@
 
 #include <stddef.h>
 
-#include "../../gpu_export.h"
-#include "../common/types.h"
-#include "../common/bitfield_helpers.h"
-#include "../common/logging.h"
+#include "gpu/command_buffer/common/bitfield_helpers.h"
+#include "gpu/command_buffer/common/logging.h"
+#include "gpu/command_buffer/common/types.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/common/command_buffer.h b/gpu/command_buffer/common/command_buffer.h
index 8c202ed..3078bca 100644
--- a/gpu/command_buffer/common/command_buffer.h
+++ b/gpu/command_buffer/common/command_buffer.h
@@ -5,9 +5,9 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_H_
 #define GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_H_
 
-#include "../../gpu_export.h"
-#include "../common/buffer.h"
-#include "../common/constants.h"
+#include "gpu/command_buffer/common/buffer.h"
+#include "gpu/command_buffer/common/constants.h"
+#include "gpu/gpu_export.h"
 
 namespace base {
 class SharedMemory;
@@ -18,10 +18,6 @@
 // Common interface for CommandBuffer implementations.
 class GPU_EXPORT CommandBuffer {
  public:
-  enum {
-    kMaxCommandBufferSize = 4 * 1024 * 1024
-  };
-
   struct State {
     State()
         : num_entries(0),
diff --git a/gpu/command_buffer/common/command_buffer_mock.h b/gpu/command_buffer/common/command_buffer_mock.h
index d02da47..0e78b7b 100644
--- a/gpu/command_buffer/common/command_buffer_mock.h
+++ b/gpu/command_buffer/common/command_buffer_mock.h
@@ -5,7 +5,7 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_MOCK_H_
 #define GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_MOCK_H_
 
-#include "../common/command_buffer.h"
+#include "gpu/command_buffer/common/command_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace base {
diff --git a/gpu/command_buffer/common/constants.h b/gpu/command_buffer/common/constants.h
index 491f443..ab25987 100644
--- a/gpu/command_buffer/common/constants.h
+++ b/gpu/command_buffer/common/constants.h
@@ -5,7 +5,7 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_CONSTANTS_H_
 #define GPU_COMMAND_BUFFER_COMMON_CONSTANTS_H_
 
-#include "../common/types.h"
+#include "gpu/command_buffer/common/types.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/common/debug_marker_manager.h b/gpu/command_buffer/common/debug_marker_manager.h
index d7f8389..65e0cd0 100644
--- a/gpu/command_buffer/common/debug_marker_manager.h
+++ b/gpu/command_buffer/common/debug_marker_manager.h
@@ -7,7 +7,7 @@
 
 #include <stack>
 #include <string>
-#include "../../gpu_export.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/common/gles2_cmd_format.cc b/gpu/command_buffer/common/gles2_cmd_format.cc
index fabdf79..5b437a5 100644
--- a/gpu/command_buffer/common/gles2_cmd_format.cc
+++ b/gpu/command_buffer/common/gles2_cmd_format.cc
@@ -7,12 +7,12 @@
 
 // We explicitly do NOT include gles2_cmd_format.h here because client side
 // and service side have different requirements.
-#include "../common/cmd_buffer_common.h"
+#include "gpu/command_buffer/common/cmd_buffer_common.h"
 
 namespace gpu {
 namespace gles2 {
 
-#include "../common/gles2_cmd_ids_autogen.h"
+#include "gpu/command_buffer/common/gles2_cmd_ids_autogen.h"
 
 const char* GetCommandName(CommandId id) {
   static const char* const names[] = {
diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h
index 9491d98..76bb3fe 100644
--- a/gpu/command_buffer/common/gles2_cmd_format.h
+++ b/gpu/command_buffer/common/gles2_cmd_format.h
@@ -13,10 +13,10 @@
 #include <string.h>
 
 #include "base/safe_numerics.h"
-#include "../common/types.h"
-#include "../common/bitfield_helpers.h"
-#include "../common/cmd_buffer_common.h"
-#include "../common/gles2_cmd_ids.h"
+#include "gpu/command_buffer/common/bitfield_helpers.h"
+#include "gpu/command_buffer/common/cmd_buffer_common.h"
+#include "gpu/command_buffer/common/gles2_cmd_ids.h"
+#include "gpu/command_buffer/common/types.h"
 
 // GL types are forward declared to avoid including the GL headers. The problem
 // is determining which GL headers to include from code that is common to the
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index 2e2b736..12fc743 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -9449,30 +9449,34 @@
     header.SetCmd<ValueType>();
   }
 
-  void Init(GLuint _width, GLuint _height) {
+  void Init(GLuint _width, GLuint _height, GLfloat _scale_factor) {
     SetHeader();
     width = _width;
     height = _height;
+    scale_factor = _scale_factor;
   }
 
-  void* Set(void* cmd, GLuint _width, GLuint _height) {
-    static_cast<ValueType*>(cmd)->Init(_width, _height);
+  void* Set(void* cmd, GLuint _width, GLuint _height, GLfloat _scale_factor) {
+    static_cast<ValueType*>(cmd)->Init(_width, _height, _scale_factor);
     return NextCmdAddress<ValueType>(cmd);
   }
 
   gpu::CommandHeader header;
   uint32 width;
   uint32 height;
+  float scale_factor;
 };
 
-COMPILE_ASSERT(sizeof(ResizeCHROMIUM) == 12,
-               Sizeof_ResizeCHROMIUM_is_not_12);
+COMPILE_ASSERT(sizeof(ResizeCHROMIUM) == 16,
+               Sizeof_ResizeCHROMIUM_is_not_16);
 COMPILE_ASSERT(offsetof(ResizeCHROMIUM, header) == 0,
                OffsetOf_ResizeCHROMIUM_header_not_0);
 COMPILE_ASSERT(offsetof(ResizeCHROMIUM, width) == 4,
                OffsetOf_ResizeCHROMIUM_width_not_4);
 COMPILE_ASSERT(offsetof(ResizeCHROMIUM, height) == 8,
                OffsetOf_ResizeCHROMIUM_height_not_8);
+COMPILE_ASSERT(offsetof(ResizeCHROMIUM, scale_factor) == 12,
+               OffsetOf_ResizeCHROMIUM_scale_factor_not_12);
 
 struct GetRequestableExtensionsCHROMIUM {
   typedef GetRequestableExtensionsCHROMIUM ValueType;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index e11bc47..b23b569 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -3706,12 +3706,14 @@
   void* next_cmd = cmd.Set(
       &cmd,
       static_cast<GLuint>(11),
-      static_cast<GLuint>(12));
+      static_cast<GLuint>(12),
+      static_cast<GLfloat>(13));
   EXPECT_EQ(static_cast<uint32>(cmds::ResizeCHROMIUM::kCmdId),
             cmd.header.command);
   EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
   EXPECT_EQ(static_cast<GLuint>(11), cmd.width);
   EXPECT_EQ(static_cast<GLuint>(12), cmd.height);
+  EXPECT_EQ(static_cast<GLfloat>(13), cmd.scale_factor);
   CheckBytesWrittenMatchesExpectedSize(
       next_cmd, sizeof(cmd));
 }
diff --git a/gpu/command_buffer/common/gles2_cmd_ids.h b/gpu/command_buffer/common/gles2_cmd_ids.h
index f5dfae3..b701c91 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids.h
@@ -7,12 +7,12 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_IDS_H_
 #define GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_IDS_H_
 
-#include "../common/cmd_buffer_common.h"
+#include "gpu/command_buffer/common/cmd_buffer_common.h"
 
 namespace gpu {
 namespace gles2 {
 
-#include "../common/gles2_cmd_ids_autogen.h"
+#include "gpu/command_buffer/common/gles2_cmd_ids_autogen.h"
 
 const char* GetCommandName(CommandId command_id);
 
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc
index 7bbed41..ef0f3ea 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -10,8 +10,8 @@
 #include <GLES2/gl2ext.h>
 #include <GLES2/gl2extchromium.h>
 
-#include "../common/gles2_cmd_utils.h"
-#include "../common/gles2_cmd_format.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
 
 namespace gpu {
 namespace gles2 {
@@ -798,7 +798,7 @@
   return true;
 }
 
-#include "../common/gles2_cmd_utils_implementation_autogen.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h"
 
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h
index 525a560..b821600 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -12,8 +12,8 @@
 #include <string>
 #include <vector>
 
-#include "../common/types.h"
 #include "gpu/command_buffer/common/gles2_utils_export.h"
+#include "gpu/command_buffer/common/types.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index e78db86..1db8acf 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -41,6 +41,7 @@
   { 0x0003, "GL_LINE_STRIP", },
   { 0x0000, "GL_POINTS", },
   { 0x0001, "GL_LINES", },
+  { 0x78F0, "GL_IMAGE_ROWBYTES_CHROMIUM", },
   { 0x88B8, "GL_READ_ONLY", },
   { 0x88B9, "GL_WRITE_ONLY_OES", },
   { 0x8211, "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT", },
diff --git a/gpu/command_buffer/common/id_allocator.cc b/gpu/command_buffer/common/id_allocator.cc
index e3fbdd5..b881e05 100644
--- a/gpu/command_buffer/common/id_allocator.cc
+++ b/gpu/command_buffer/common/id_allocator.cc
@@ -4,8 +4,8 @@
 
 // This file contains the implementation of IdAllocator.
 
-#include "../common/id_allocator.h"
-#include "../common/logging.h"
+#include "gpu/command_buffer/common/id_allocator.h"
+#include "gpu/command_buffer/common/logging.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/common/id_allocator.h b/gpu/command_buffer/common/id_allocator.h
index 7e25dbb..46fcd1a 100644
--- a/gpu/command_buffer/common/id_allocator.h
+++ b/gpu/command_buffer/common/id_allocator.h
@@ -11,9 +11,8 @@
 #include <utility>
 
 #include "base/compiler_specific.h"
-
-#include "../../gpu_export.h"
-#include "../common/types.h"
+#include "gpu/command_buffer/common/types.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/common/mailbox.cc b/gpu/command_buffer/common/mailbox.cc
index c47a9fd..653d1d6 100644
--- a/gpu/command_buffer/common/mailbox.cc
+++ b/gpu/command_buffer/common/mailbox.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "../common/mailbox.h"
+#include "gpu/command_buffer/common/mailbox.h"
 
 #include <string.h>
 
-#include "../common/logging.h"
+#include "gpu/command_buffer/common/logging.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/common/mailbox.h b/gpu/command_buffer/common/mailbox.h
index c0ae06f..ca24a71 100644
--- a/gpu/command_buffer/common/mailbox.h
+++ b/gpu/command_buffer/common/mailbox.h
@@ -5,8 +5,8 @@
 #ifndef GPU_COMMAND_BUFFER_MAILBOX_H_
 #define GPU_COMMAND_BUFFER_MAILBOX_H_
 
-#include "../../gpu_export.h"
-#include "../common/types.h"
+#include "gpu/command_buffer/common/types.h"
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/gles2_utils.target.darwin-arm.mk b/gpu/command_buffer/gles2_utils.target.darwin-arm.mk
index 525cd2f..c23333a 100644
--- a/gpu/command_buffer/gles2_utils.target.darwin-arm.mk
+++ b/gpu/command_buffer/gles2_utils.target.darwin-arm.mk
@@ -69,6 +69,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -95,9 +96,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer/gles2_utils.target.darwin-x86.mk b/gpu/command_buffer/gles2_utils.target.darwin-x86.mk
index ac8efd1..97863b8 100644
--- a/gpu/command_buffer/gles2_utils.target.darwin-x86.mk
+++ b/gpu/command_buffer/gles2_utils.target.darwin-x86.mk
@@ -71,6 +71,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -97,9 +98,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer/gles2_utils.target.linux-arm.mk b/gpu/command_buffer/gles2_utils.target.linux-arm.mk
index 525cd2f..c23333a 100644
--- a/gpu/command_buffer/gles2_utils.target.linux-arm.mk
+++ b/gpu/command_buffer/gles2_utils.target.linux-arm.mk
@@ -69,6 +69,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -95,9 +96,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer/gles2_utils.target.linux-x86.mk b/gpu/command_buffer/gles2_utils.target.linux-x86.mk
index ac8efd1..97863b8 100644
--- a/gpu/command_buffer/gles2_utils.target.linux-x86.mk
+++ b/gpu/command_buffer/gles2_utils.target.linux-x86.mk
@@ -71,6 +71,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -97,9 +98,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate.cc
index fb63e21..9df9c60 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate.cc
@@ -25,6 +25,25 @@
 
 }  // namespace
 
+AsyncPixelTransferUploadStats::AsyncPixelTransferUploadStats()
+    : texture_upload_count_(0) {}
+
+AsyncPixelTransferUploadStats::~AsyncPixelTransferUploadStats() {}
+
+void AsyncPixelTransferUploadStats::AddUpload(base::TimeDelta transfer_time) {
+  base::AutoLock scoped_lock(lock_);
+  texture_upload_count_++;
+  total_texture_upload_time_ += transfer_time;
+}
+
+int AsyncPixelTransferUploadStats::GetStats(
+    base::TimeDelta* total_texture_upload_time) {
+  base::AutoLock scoped_lock(lock_);
+  if (total_texture_upload_time)
+    *total_texture_upload_time = total_texture_upload_time_;
+  return texture_upload_count_;
+}
+
 AsyncPixelTransferState::AsyncPixelTransferState(){}
 
 AsyncPixelTransferState::~AsyncPixelTransferState(){}
@@ -52,4 +71,4 @@
                         mem_params.shm_data_size);
 }
 
-}// namespace gpu
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate.h b/gpu/command_buffer/service/async_pixel_transfer_delegate.h
index 75f3ad2..b21a63b 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate.h
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate.h
@@ -9,6 +9,7 @@
 #include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
 #include "base/time.h"
 #include "gpu/gpu_export.h"
 #include "ui/gl/gl_bindings.h"
@@ -50,6 +51,26 @@
   uint32 shm_data_size;
 };
 
+class AsyncPixelTransferUploadStats
+    : public base::RefCountedThreadSafe<AsyncPixelTransferUploadStats> {
+ public:
+  AsyncPixelTransferUploadStats();
+
+  void AddUpload(base::TimeDelta transfer_time);
+  int GetStats(base::TimeDelta* total_texture_upload_time);
+
+ private:
+  friend class base::RefCountedThreadSafe<AsyncPixelTransferUploadStats>;
+
+  ~AsyncPixelTransferUploadStats();
+
+  int texture_upload_count_;
+  base::TimeDelta total_texture_upload_time_;
+  base::Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferUploadStats);
+};
+
 // AsyncPixelTransferState holds the resources required to do async
 // transfers on one texture. It should stay alive for the lifetime
 // of the texture to allow multiple transfers.
@@ -62,7 +83,6 @@
   virtual bool TransferIsInProgress() = 0;
 
  protected:
-  friend class base::RefCounted<AsyncPixelTransferState>;
   AsyncPixelTransferState();
 
  private:
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc
index 2d834c9..fa29cf6 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc
@@ -12,6 +12,16 @@
 #include "ui/gl/gl_implementation.h"
 
 namespace gpu {
+namespace {
+
+bool IsBroadcom() {
+  const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
+  if (vendor)
+    return std::string(vendor).find("Broadcom") != std::string::npos;
+  return false;
+}
+
+}
 
 // We only used threaded uploads when we can:
 // - Create EGLImages out of OpenGL textures (EGL_KHR_gl_texture_2D_image)
@@ -27,7 +37,8 @@
           context->HasExtension("EGL_KHR_image") &&
           context->HasExtension("EGL_KHR_image_base") &&
           context->HasExtension("EGL_KHR_gl_texture_2D_image") &&
-          context->HasExtension("GL_OES_EGL_image")) {
+          context->HasExtension("GL_OES_EGL_image") &&
+          !IsBroadcom()) {
         return new AsyncPixelTransferDelegateEGL;
       }
       LOG(INFO) << "Async pixel transfers not supported";
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc
index 6839dda..c56f72a 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc
@@ -18,11 +18,6 @@
 #include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/scoped_binders.h"
 
-#if defined(OS_ANDROID)
-// TODO(epenner): Move thread priorities to base. (crbug.com/170549)
-#include <sys/resource.h>
-#endif
-
 namespace gpu {
 
 namespace {
@@ -72,10 +67,22 @@
       tex_params.format, tex_params.type, data);
 }
 
+void SetGlParametersForEglImageTexture() {
+  // These params are needed for EGLImage creation to succeed on several
+  // Android devices. I couldn't find this requirement in the EGLImage
+  // extension spec, but several devices fail without it.
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
 class TransferThread : public base::Thread {
  public:
   TransferThread() : base::Thread(kAsyncTransferThreadName) {
     Start();
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+    SetPriority(base::kThreadPriority_Background);
+#endif
   }
   virtual ~TransferThread() {
     Stop();
@@ -91,13 +98,6 @@
                                                gfx::PreferDiscreteGpu);
     bool is_current = context_->MakeCurrent(surface_);
     DCHECK(is_current);
-
-#if defined(OS_ANDROID)
-    // TODO(epenner): Move thread priorities to base. (crbug.com/170549)
-    int nice_value = 10; // Idle priority.
-    setpriority(PRIO_PROCESS, base::PlatformThread::CurrentId(), nice_value);
-#endif
-
   }
 
   virtual void CleanUp() OVERRIDE {
@@ -130,8 +130,6 @@
   return g_transfer_thread.Pointer()->safe_shared_memory_pool();
 }
 
-}  // namespace
-
 // Class which holds async pixel transfers state (EGLImage).
 // The EGLImage is accessed by either thread, but everything
 // else accessed only on the main thread.
@@ -164,7 +162,7 @@
     DCHECK(texture_id_);
     DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_);
 
-    gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_);
+    glBindTexture(GL_TEXTURE_2D, texture_id_);
     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
     bind_callback_.Run();
 
@@ -232,12 +230,110 @@
   }
 
   void WaitForTransferCompletion() {
+    TRACE_EVENT0("gpu", "WaitForTransferCompletion");
+    // TODO(backer): Deschedule the channel rather than blocking the main GPU
+    // thread (crbug.com/240265).
     transfer_completion_.Wait();
   }
 
+  void PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params,
+                              AsyncMemoryParams mem_params,
+                              ScopedSafeSharedMemory* safe_shared_memory) {
+    TRACE_EVENT2("gpu",
+                 "PerformAsyncTexImage",
+                 "width",
+                 tex_params.width,
+                 "height",
+                 tex_params.height);
+    DCHECK(!thread_texture_id_);
+    DCHECK_EQ(0, tex_params.level);
+    DCHECK_EQ(EGL_NO_IMAGE_KHR, egl_image_);
+
+    void* data =
+        AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params);
+    {
+      TRACE_EVENT0("gpu", "glTexImage2D no data");
+      glGenTextures(1, &thread_texture_id_);
+      glActiveTexture(GL_TEXTURE0);
+      glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
+
+      SetGlParametersForEglImageTexture();
+
+      // If we need to use image_preserved, we pass the data with
+      // the allocation. Otherwise we use a NULL allocation to
+      // try to avoid any costs associated with creating the EGLImage.
+      if (use_image_preserved_)
+        DoTexImage2D(tex_params, data);
+      else
+        DoTexImage2D(tex_params, NULL);
+    }
+
+    CreateEglImageOnUploadThread();
+
+    {
+      TRACE_EVENT0("gpu", "glTexSubImage2D with data");
+
+      // If we didn't use image_preserved, we haven't uploaded
+      // the data yet, so we do this with a full texSubImage.
+      if (!use_image_preserved_)
+        DoFullTexSubImage2D(tex_params, data);
+    }
+
+    WaitForLastUpload();
+    MarkAsCompleted();
+
+    DCHECK(CHECK_GL());
+  }
+
+  void PerformAsyncTexSubImage2D(
+      AsyncTexSubImage2DParams tex_params,
+      AsyncMemoryParams mem_params,
+      ScopedSafeSharedMemory* safe_shared_memory,
+      scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) {
+    TRACE_EVENT2("gpu",
+                 "PerformAsyncTexSubImage2D",
+                 "width",
+                 tex_params.width,
+                 "height",
+                 tex_params.height);
+
+    DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_);
+    DCHECK_EQ(0, tex_params.level);
+
+    void* data =
+        AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params);
+
+    base::TimeTicks begin_time;
+    if (texture_upload_stats)
+      begin_time = base::TimeTicks::HighResNow();
+
+    if (!thread_texture_id_) {
+      TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES");
+      glGenTextures(1, &thread_texture_id_);
+      glActiveTexture(GL_TEXTURE0);
+      glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
+      glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
+    } else {
+      glActiveTexture(GL_TEXTURE0);
+      glBindTexture(GL_TEXTURE_2D, thread_texture_id_);
+    }
+    {
+      TRACE_EVENT0("gpu", "glTexSubImage2D");
+      DoTexSubImage2D(tex_params, data);
+    }
+    WaitForLastUpload();
+    MarkAsCompleted();
+
+    DCHECK(CHECK_GL());
+    if (texture_upload_stats) {
+      texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
+                                      begin_time);
+    }
+  }
+
  protected:
   friend class base::RefCountedThreadSafe<TransferStateInternal>;
-  friend class AsyncPixelTransferDelegateEGL;
+  friend class gpu::AsyncPixelTransferDelegateEGL;
 
   static void DeleteTexture(GLuint id) {
     glDeleteTextures(1, &id);
@@ -282,38 +378,6 @@
   bool use_image_preserved_;
 };
 
-class TextureUploadStats
-    : public base::RefCountedThreadSafe<TextureUploadStats> {
- public:
-  TextureUploadStats() : texture_upload_count_(0) {}
-
-  void AddUpload(base::TimeDelta transfer_time) {
-    base::AutoLock scoped_lock(lock_);
-    texture_upload_count_++;
-    total_texture_upload_time_ += transfer_time;
-  }
-
-  int GetStats(base::TimeDelta* total_texture_upload_time) {
-    base::AutoLock scoped_lock(lock_);
-    if (total_texture_upload_time)
-      *total_texture_upload_time = total_texture_upload_time_;
-    return texture_upload_count_;
-  }
-
- private:
-  friend class base::RefCountedThreadSafe<TextureUploadStats>;
-
-  ~TextureUploadStats() {}
-
-  int texture_upload_count_;
-  base::TimeDelta total_texture_upload_time_;
-  base::Lock lock_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextureUploadStats);
-};
-
-namespace {
-
 // EGL needs thread-safe ref-counting, so this just wraps
 // an internal thread-safe ref-counted state object.
 class AsyncTransferStateImpl : public AsyncPixelTransferState {
@@ -350,7 +414,7 @@
   is_imagination_ = vendor.find("Imagination") != std::string::npos;
   is_qualcomm_ = vendor.find("Qualcomm") != std::string::npos;
   // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present.
-  texture_upload_stats_ = make_scoped_refptr(new TextureUploadStats);
+  texture_upload_stats_ = make_scoped_refptr(new AsyncPixelTransferUploadStats);
 }
 
 AsyncPixelTransferDelegateEGL::~AsyncPixelTransferDelegateEGL() {}
@@ -382,6 +446,8 @@
 }
 
 void AsyncPixelTransferDelegateEGL::BindCompletedAsyncTransfers() {
+  scoped_ptr<gfx::ScopedTextureBinder> texture_binder;
+
   while(!pending_allocations_.empty()) {
     if (!pending_allocations_.front().get()) {
       pending_allocations_.pop_front();
@@ -393,6 +459,10 @@
     // Terminate early, as all transfers finish in order, currently.
     if (state->TransferIsInProgress())
       break;
+
+    if (!texture_binder)
+      texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0));
+
     // If the transfer is finished, bind it to the texture
     // and remove it from pending list.
     state->BindTransfer();
@@ -421,30 +491,21 @@
 
 void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion(
       AsyncPixelTransferState* transfer_state) {
-  TRACE_EVENT0("gpu", "WaitForTransferCompletion");
   scoped_refptr<TransferStateInternal> state =
       static_cast<AsyncTransferStateImpl*>(transfer_state)->internal_.get();
   DCHECK(state);
   DCHECK(state->texture_id_);
 
   if (state->TransferIsInProgress()) {
-#if defined(OS_ANDROID)
-    // TODO(epenner): Move thread priorities to base. (crbug.com/170549)
-    int default_nice_value = 0;  // Default priority.
-    int idle_nice_value    = 10; // Idle priority.
-    setpriority(PRIO_PROCESS,
-                g_transfer_thread.Pointer()->thread_id(),
-                default_nice_value);
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+    g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Display);
 #endif
 
     state->WaitForTransferCompletion();
     DCHECK(!state->TransferIsInProgress());
 
-#if defined(OS_ANDROID)
-    // TODO(epenner): Move thread priorities to base. (crbug.com/170549)
-    setpriority(PRIO_PROCESS,
-                g_transfer_thread.Pointer()->thread_id(),
-                idle_nice_value);
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+    g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Background);
 #endif
   }
 }
@@ -482,7 +543,7 @@
   // a use-after-free of the raw pixels.
   transfer_message_loop_proxy()->PostTask(FROM_HERE,
       base::Bind(
-          &AsyncPixelTransferDelegateEGL::PerformAsyncTexImage2D,
+          &TransferStateInternal::PerformAsyncTexImage2D,
           state,
           tex_params,
           mem_params,
@@ -525,7 +586,7 @@
   // a use-after-free of the raw pixels.
   transfer_message_loop_proxy()->PostTask(FROM_HERE,
       base::Bind(
-          &AsyncPixelTransferDelegateEGL::PerformAsyncTexSubImage2D,
+          &TransferStateInternal::PerformAsyncTexSubImage2D,
           state,
           tex_params,
           mem_params,
@@ -556,17 +617,6 @@
   return false;
 }
 
-namespace {
-void SetGlParametersForEglImageTexture() {
-  // These params are needed for EGLImage creation to succeed on several
-  // Android devices. I couldn't find this requirement in the EGLImage
-  // extension spec, but several devices fail without it.
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-}
-} // namespace
-
 void AsyncPixelTransferDelegateEGL::PerformNotifyCompletion(
     AsyncMemoryParams mem_params,
     ScopedSafeSharedMemory* safe_shared_memory,
@@ -577,98 +627,6 @@
   callback.Run(safe_mem_params);
 }
 
-void AsyncPixelTransferDelegateEGL::PerformAsyncTexImage2D(
-    TransferStateInternal* state,
-    AsyncTexImage2DParams tex_params,
-    AsyncMemoryParams mem_params,
-    ScopedSafeSharedMemory* safe_shared_memory) {
-  TRACE_EVENT2("gpu", "PerformAsyncTexImage",
-               "width", tex_params.width,
-               "height", tex_params.height);
-  DCHECK(state);
-  DCHECK(!state->thread_texture_id_);
-  DCHECK_EQ(0, tex_params.level);
-  DCHECK_EQ(EGL_NO_IMAGE_KHR, state->egl_image_);
-
-  void* data = GetAddress(safe_shared_memory, mem_params);
-  {
-    TRACE_EVENT0("gpu", "glTexImage2D no data");
-    glGenTextures(1, &state->thread_texture_id_);
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, state->thread_texture_id_);
-
-    SetGlParametersForEglImageTexture();
-
-    // If we need to use image_preserved, we pass the data with
-    // the allocation. Otherwise we use a NULL allocation to
-    // try to avoid any costs associated with creating the EGLImage.
-    if (state->use_image_preserved_)
-       DoTexImage2D(tex_params, data);
-    else
-       DoTexImage2D(tex_params, NULL);
-  }
-
-  state->CreateEglImageOnUploadThread();
-
-  {
-    TRACE_EVENT0("gpu", "glTexSubImage2D with data");
-
-    // If we didn't use image_preserved, we haven't uploaded
-    // the data yet, so we do this with a full texSubImage.
-    if (!state->use_image_preserved_)
-      DoFullTexSubImage2D(tex_params, data);
-  }
-
-  state->WaitForLastUpload();
-  state->MarkAsCompleted();
-
-  DCHECK(CHECK_GL());
-}
-
-void AsyncPixelTransferDelegateEGL::PerformAsyncTexSubImage2D(
-    TransferStateInternal* state,
-    AsyncTexSubImage2DParams tex_params,
-    AsyncMemoryParams mem_params,
-    ScopedSafeSharedMemory* safe_shared_memory,
-    scoped_refptr<TextureUploadStats> texture_upload_stats) {
-  TRACE_EVENT2("gpu", "PerformAsyncTexSubImage2D",
-               "width", tex_params.width,
-               "height", tex_params.height);
-
-  DCHECK(state);
-  DCHECK_NE(EGL_NO_IMAGE_KHR, state->egl_image_);
-  DCHECK_EQ(0, tex_params.level);
-
-  void* data = GetAddress(safe_shared_memory, mem_params);
-
-  base::TimeTicks begin_time;
-  if (texture_upload_stats)
-    begin_time = base::TimeTicks::HighResNow();
-
-  if (!state->thread_texture_id_) {
-    TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES");
-    glGenTextures(1, &state->thread_texture_id_);
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, state->thread_texture_id_);
-    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, state->egl_image_);
-  } else {
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, state->thread_texture_id_);
-  }
-  {
-    TRACE_EVENT0("gpu", "glTexSubImage2D");
-    DoTexSubImage2D(tex_params, data);
-  }
-  state->WaitForLastUpload();
-  state->MarkAsCompleted();
-
-  DCHECK(CHECK_GL());
-  if (texture_upload_stats) {
-    texture_upload_stats->AddUpload(
-        base::TimeTicks::HighResNow() - begin_time);
-  }
-}
-
 namespace {
 bool IsPowerOfTwo (unsigned int x) {
   return ((x != 0) && !(x & (x - 1)));
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h
index 39a63cd..67cd971 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.h
@@ -11,8 +11,6 @@
 
 namespace gpu {
 class ScopedSafeSharedMemory;
-class TextureUploadStats;
-class TransferStateInternal;
 
 // Class which handles async pixel transfers using EGLImageKHR and another
 // upload thread
@@ -52,17 +50,6 @@
       AsyncMemoryParams mem_params,
       ScopedSafeSharedMemory* safe_shared_memory,
       const CompletionCallback& callback);
-  static void PerformAsyncTexImage2D(
-      TransferStateInternal* state,
-      AsyncTexImage2DParams tex_params,
-      AsyncMemoryParams mem_params,
-      ScopedSafeSharedMemory* safe_shared_memory);
-  static void PerformAsyncTexSubImage2D(
-      TransferStateInternal* state,
-      AsyncTexSubImage2DParams tex_params,
-      AsyncMemoryParams mem_params,
-      ScopedSafeSharedMemory* safe_shared_memory,
-      scoped_refptr<TextureUploadStats> texture_upload_stats);
 
   // Returns true if a work-around was used.
   bool WorkAroundAsyncTexImage2D(
@@ -78,7 +65,7 @@
   typedef std::list<base::WeakPtr<AsyncPixelTransferState> > TransferQueue;
   TransferQueue pending_allocations_;
 
-  scoped_refptr<TextureUploadStats> texture_upload_stats_;
+  scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats_;
   bool is_imagination_;
   bool is_qualcomm_;
 
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc
index 76a3e4b..470ac1d 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_linux.cc
@@ -4,15 +4,25 @@
 
 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
 
+#include "base/command_line.h"
 #include "base/debug/trace_event.h"
 #include "gpu/command_buffer/service/async_pixel_transfer_delegate_idle.h"
+#include "gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h"
 #include "gpu/command_buffer/service/async_pixel_transfer_delegate_stub.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
 #include "ui/gl/gl_implementation.h"
 
 namespace gpu {
 
 AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Create(
     gfx::GLContext* context) {
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableShareGroupAsyncTextureUpload)) {
+    DCHECK(context);
+    return static_cast<AsyncPixelTransferDelegate*> (
+        new AsyncPixelTransferDelegateShareGroup(context));
+  }
+
   TRACE_EVENT0("gpu", "AsyncPixelTransferDelegate::Create");
   switch (gfx::GetGLImplementation()) {
     case gfx::kGLImplementationOSMesaGL:
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc
new file mode 100644
index 0000000..a71bd66
--- /dev/null
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc
@@ -0,0 +1,504 @@
+// Copyright 2013 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 "gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
+#include "gpu/command_buffer/service/safe_shared_memory_pool.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/gpu_preference.h"
+#include "ui/gl/scoped_binders.h"
+
+namespace gpu {
+
+namespace {
+
+const char kAsyncTransferThreadName[] = "AsyncTransferThread";
+
+// TODO(backer): Factor out common thread scheduling logic from the EGL and
+// ShareGroup implementations. http://crbug.com/239889
+class TransferThread : public base::Thread {
+ public:
+  TransferThread()
+      : base::Thread(kAsyncTransferThreadName),
+        initialized_(false) {
+    Start();
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+    SetPriority(base::kThreadPriority_Background);
+#endif
+  }
+
+  virtual ~TransferThread() {
+    // The only instance of this class was declared leaky.
+    NOTREACHED();
+  }
+
+  void InitializeOnMainThread(gfx::GLContext* parent_context) {
+    TRACE_EVENT0("gpu", "TransferThread::InitializeOnMainThread");
+    if (initialized_)
+      return;
+
+    base::WaitableEvent wait_for_init(true, false);
+    message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&TransferThread::InitializeOnTransferThread,
+                 base::Unretained(this),
+                 base::Unretained(parent_context),
+                 &wait_for_init));
+    wait_for_init.Wait();
+  }
+
+  virtual void CleanUp() OVERRIDE {
+    surface_ = NULL;
+    context_ = NULL;
+  }
+
+  SafeSharedMemoryPool* safe_shared_memory_pool() {
+    return &safe_shared_memory_pool_;
+  }
+
+ private:
+  bool initialized_;
+
+  scoped_refptr<gfx::GLSurface> surface_;
+  scoped_refptr<gfx::GLContext> context_;
+  SafeSharedMemoryPool safe_shared_memory_pool_;
+
+  void InitializeOnTransferThread(gfx::GLContext* parent_context,
+                                   base::WaitableEvent* caller_wait) {
+    TRACE_EVENT0("gpu", "InitializeOnTransferThread");
+
+    if (!parent_context) {
+      LOG(ERROR) << "No parent context provided.";
+      caller_wait->Signal();
+      return;
+    }
+
+    surface_ = gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1));
+    if (!surface_) {
+      LOG(ERROR) << "Unable to create GLSurface";
+      caller_wait->Signal();
+      return;
+    }
+
+    // TODO(backer): This is coded for integrated GPUs. For discrete GPUs
+    // we would probably want to use a PBO texture upload for a true async
+    // upload (that would hopefully be optimized as a DMA transfer by the
+    // driver).
+    context_ = gfx::GLContext::CreateGLContext(parent_context->share_group(),
+                                               surface_,
+                                               gfx::PreferIntegratedGpu);
+    if (!context_) {
+      LOG(ERROR) << "Unable to create GLContext.";
+      caller_wait->Signal();
+      return;
+    }
+
+    context_->MakeCurrent(surface_);
+    initialized_ = true;
+    caller_wait->Signal();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(TransferThread);
+};
+
+base::LazyInstance<TransferThread>::Leaky
+    g_transfer_thread = LAZY_INSTANCE_INITIALIZER;
+
+base::MessageLoopProxy* transfer_message_loop_proxy() {
+  return g_transfer_thread.Pointer()->message_loop_proxy();
+}
+
+SafeSharedMemoryPool* safe_shared_memory_pool() {
+  return g_transfer_thread.Pointer()->safe_shared_memory_pool();
+}
+
+// Class which holds async pixel transfers state.
+// The texture_id is accessed by either thread, but everything
+// else accessed only on the main thread.
+class TransferStateInternal
+    : public base::RefCountedThreadSafe<TransferStateInternal> {
+ public:
+  TransferStateInternal(GLuint texture_id,
+                        const AsyncTexImage2DParams& define_params)
+      : texture_id_(texture_id),
+        transfer_completion_(true, true) {
+    define_params_ = define_params;
+  }
+
+  // Implement AsyncPixelTransferState:
+  bool TransferIsInProgress() {
+    return !transfer_completion_.IsSignaled();
+  }
+
+  void BindTransfer() {
+    TRACE_EVENT2("gpu", "BindAsyncTransfer",
+                 "width", define_params_.width,
+                 "height", define_params_.height);
+    DCHECK(texture_id_);
+
+    glBindTexture(GL_TEXTURE_2D, texture_id_);
+    bind_callback_.Run();
+  }
+
+  void MarkAsTransferIsInProgress() {
+    transfer_completion_.Reset();
+  }
+
+  void MarkAsCompleted() {
+    TRACE_EVENT0("gpu", "MarkAsCompleted");
+    glFlush();
+    transfer_completion_.Signal();
+  }
+
+  void WaitForTransferCompletion() {
+    TRACE_EVENT0("gpu", "WaitForTransferCompletion");
+    // TODO(backer): Deschedule the channel rather than blocking the main GPU
+    // thread (crbug.com/240265).
+    transfer_completion_.Wait();
+  }
+
+  GLuint texture_id() { return texture_id_; }
+
+  void SetBindCallback(base::Closure bind_callback) {
+    bind_callback_ = bind_callback;
+  }
+
+  void PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params,
+                              AsyncMemoryParams mem_params,
+                              ScopedSafeSharedMemory* safe_shared_memory) {
+    base::AutoLock locked(upload_lock_);
+    if (cancel_upload_flag_.IsSet())
+      return;
+
+    TRACE_EVENT2("gpu",
+                 "PerformAsyncTexImage",
+                 "width",
+                 tex_params.width,
+                 "height",
+                 tex_params.height);
+    DCHECK_EQ(0, tex_params.level);
+
+    void* data =
+        AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params);
+
+    {
+      TRACE_EVENT0("gpu", "glTexImage2D");
+      glBindTexture(GL_TEXTURE_2D, texture_id_);
+      glTexImage2D(GL_TEXTURE_2D,
+                   tex_params.level,
+                   tex_params.internal_format,
+                   tex_params.width,
+                   tex_params.height,
+                   tex_params.border,
+                   tex_params.format,
+                   tex_params.type,
+                   data);
+      glBindTexture(GL_TEXTURE_2D, 0);
+    }
+
+    MarkAsCompleted();
+  }
+
+  void PerformAsyncTexSubImage2D(
+      AsyncTexSubImage2DParams tex_params,
+      AsyncMemoryParams mem_params,
+      ScopedSafeSharedMemory* safe_shared_memory,
+      scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) {
+    base::AutoLock locked(upload_lock_);
+    if (cancel_upload_flag_.IsSet())
+      return;
+
+    TRACE_EVENT2("gpu",
+                 "PerformAsyncTexSubImage2D",
+                 "width",
+                 tex_params.width,
+                 "height",
+                 tex_params.height);
+    DCHECK_EQ(0, tex_params.level);
+
+    base::TimeTicks begin_time;
+    if (texture_upload_stats)
+      begin_time = base::TimeTicks::HighResNow();
+
+    void* data =
+        AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params);
+
+    {
+      TRACE_EVENT0("gpu", "glTexSubImage2D");
+      glBindTexture(GL_TEXTURE_2D, texture_id_);
+      glTexSubImage2D(GL_TEXTURE_2D,
+                      tex_params.level,
+                      tex_params.xoffset,
+                      tex_params.yoffset,
+                      tex_params.width,
+                      tex_params.height,
+                      tex_params.format,
+                      tex_params.type,
+                      data);
+      glBindTexture(GL_TEXTURE_2D, 0);
+    }
+
+    MarkAsCompleted();
+
+    if (texture_upload_stats) {
+      texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() -
+                                      begin_time);
+    }
+  }
+
+  base::CancellationFlag* cancel_upload_flag() { return &cancel_upload_flag_; }
+  base::Lock* upload_lock() { return &upload_lock_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<TransferStateInternal>;
+
+  virtual ~TransferStateInternal() {
+  }
+
+  // Used to cancel pending uploads.
+  base::CancellationFlag cancel_upload_flag_;
+  base::Lock upload_lock_;
+
+  GLuint texture_id_;
+
+  // Definition params for texture that needs binding.
+  AsyncTexImage2DParams define_params_;
+
+  // Indicates that an async transfer is in progress.
+  base::WaitableEvent transfer_completion_;
+
+  // Callback to invoke when AsyncTexImage2D is complete
+  // and the client can safely use the texture. This occurs
+  // during BindCompletedAsyncTransfers().
+  base::Closure bind_callback_;
+};
+
+void PerformNotifyCompletion(
+    AsyncMemoryParams mem_params,
+    ScopedSafeSharedMemory* safe_shared_memory,
+    const AsyncPixelTransferDelegate::CompletionCallback& callback) {
+  TRACE_EVENT0("gpu", "PerformNotifyCompletion");
+  AsyncMemoryParams safe_mem_params = mem_params;
+  safe_mem_params.shared_memory = safe_shared_memory->shared_memory();
+  callback.Run(safe_mem_params);
+}
+
+}  // namespace
+
+// ShareGroup needs thread-safe ref-counting, so this just wraps
+// an internal thread-safe ref-counted state object.
+class AsyncTransferStateImpl : public AsyncPixelTransferState {
+ public:
+  AsyncTransferStateImpl(GLuint texture_id,
+                         const AsyncTexImage2DParams& define_params)
+      : internal_(new TransferStateInternal(texture_id, define_params)) {}
+
+  virtual ~AsyncTransferStateImpl() {
+    TRACE_EVENT0("gpu", " ~AsyncTransferStateImpl");
+    base::AutoLock locked(*internal_->upload_lock());
+    internal_->cancel_upload_flag()->Set();
+  }
+
+  virtual bool TransferIsInProgress() OVERRIDE {
+      return internal_->TransferIsInProgress();
+  }
+
+  TransferStateInternal* internal() { return internal_.get(); }
+
+ private:
+  scoped_refptr<TransferStateInternal> internal_;
+};
+
+AsyncPixelTransferDelegateShareGroup::AsyncPixelTransferDelegateShareGroup(
+      gfx::GLContext* context) {
+  g_transfer_thread.Pointer()->InitializeOnMainThread(context);
+
+  // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present.
+  texture_upload_stats_ = make_scoped_refptr(new AsyncPixelTransferUploadStats);
+}
+
+AsyncPixelTransferDelegateShareGroup::~AsyncPixelTransferDelegateShareGroup() {
+}
+
+AsyncPixelTransferState*
+    AsyncPixelTransferDelegateShareGroup::
+        CreatePixelTransferState(
+            GLuint texture_id,
+            const AsyncTexImage2DParams& define_params) {
+  return static_cast<AsyncPixelTransferState*>(
+      new AsyncTransferStateImpl(texture_id, define_params));
+}
+
+void AsyncPixelTransferDelegateShareGroup::BindCompletedAsyncTransfers() {
+  scoped_ptr<gfx::ScopedTextureBinder> texture_binder;
+
+  while (!pending_allocations_.empty()) {
+    if (!pending_allocations_.front().get()) {
+      pending_allocations_.pop_front();
+      continue;
+    }
+    scoped_refptr<TransferStateInternal> state =
+        static_cast<AsyncTransferStateImpl*>
+            (pending_allocations_.front().get())->internal();
+    // Terminate early, as all transfers finish in order, currently.
+    if (state->TransferIsInProgress())
+      break;
+
+    if (!texture_binder)
+      texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0));
+
+    // Used to set tex info from the gles2 cmd decoder once upload has
+    // finished (it'll bind the texture and call a callback).
+    state->BindTransfer();
+
+    pending_allocations_.pop_front();
+  }
+}
+
+void AsyncPixelTransferDelegateShareGroup::AsyncNotifyCompletion(
+    const AsyncMemoryParams& mem_params,
+    const CompletionCallback& callback) {
+  DCHECK(mem_params.shared_memory);
+  DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size,
+            mem_params.shm_size);
+  // Post a PerformNotifyCompletion task to the upload thread. This task
+  // will run after all async transfers are complete.
+  transfer_message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&PerformNotifyCompletion,
+                 mem_params,
+                 base::Owned(
+                     new ScopedSafeSharedMemory(safe_shared_memory_pool(),
+                                                mem_params.shared_memory,
+                                                mem_params.shm_size)),
+                 callback));
+}
+
+void AsyncPixelTransferDelegateShareGroup::WaitForTransferCompletion(
+      AsyncPixelTransferState* transfer_state) {
+  scoped_refptr<TransferStateInternal> state =
+      static_cast<AsyncTransferStateImpl*>(transfer_state)->internal();
+  DCHECK(state);
+  DCHECK(state->texture_id());
+
+  if (state->TransferIsInProgress()) {
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+    g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Display);
+#endif
+
+    state->WaitForTransferCompletion();
+    DCHECK(!state->TransferIsInProgress());
+
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+    g_transfer_thread.Pointer()->SetPriority(base::kThreadPriority_Background);
+#endif
+  }
+}
+
+void AsyncPixelTransferDelegateShareGroup::AsyncTexImage2D(
+    AsyncPixelTransferState* transfer_state,
+    const AsyncTexImage2DParams& tex_params,
+    const AsyncMemoryParams& mem_params,
+    const base::Closure& bind_callback) {
+  scoped_refptr<TransferStateInternal> state =
+      static_cast<AsyncTransferStateImpl*>(transfer_state)->internal();
+  DCHECK(mem_params.shared_memory);
+  DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size,
+            mem_params.shm_size);
+  DCHECK(state);
+  DCHECK(state->texture_id());
+  DCHECK(!state->TransferIsInProgress());
+  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
+  DCHECK_EQ(tex_params.level, 0);
+
+  // Mark the transfer in progress and save the late bind
+  // callback, so we can notify the client when it is bound.
+  pending_allocations_.push_back(transfer_state->AsWeakPtr());
+  state->SetBindCallback(bind_callback);
+
+  // Mark the transfer in progress.
+  state->MarkAsTransferIsInProgress();
+
+  // Duplicate the shared memory so there is no way we can get
+  // a use-after-free of the raw pixels.
+  transfer_message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &TransferStateInternal::PerformAsyncTexImage2D,
+          state,
+          tex_params,
+          mem_params,
+          base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(),
+                                                 mem_params.shared_memory,
+                                                 mem_params.shm_size))));
+}
+
+void AsyncPixelTransferDelegateShareGroup::AsyncTexSubImage2D(
+    AsyncPixelTransferState* transfer_state,
+    const AsyncTexSubImage2DParams& tex_params,
+    const AsyncMemoryParams& mem_params) {
+  TRACE_EVENT2("gpu", "AsyncTexSubImage2D",
+               "width", tex_params.width,
+               "height", tex_params.height);
+  scoped_refptr<TransferStateInternal> state =
+      static_cast<AsyncTransferStateImpl*>(transfer_state)->internal();
+
+  DCHECK(state->texture_id());
+  DCHECK(!state->TransferIsInProgress());
+  DCHECK(mem_params.shared_memory);
+  DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size,
+            mem_params.shm_size);
+  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
+  DCHECK_EQ(tex_params.level, 0);
+
+  // Mark the transfer in progress.
+  state->MarkAsTransferIsInProgress();
+
+  // Duplicate the shared memory so there are no way we can get
+  // a use-after-free of the raw pixels.
+  transfer_message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &TransferStateInternal::PerformAsyncTexSubImage2D,
+          state,
+          tex_params,
+          mem_params,
+          base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(),
+                                                 mem_params.shared_memory,
+                                                 mem_params.shm_size)),
+          texture_upload_stats_));
+}
+
+uint32 AsyncPixelTransferDelegateShareGroup::GetTextureUploadCount() {
+  DCHECK(texture_upload_stats_);
+  return texture_upload_stats_->GetStats(NULL);
+}
+
+base::TimeDelta
+    AsyncPixelTransferDelegateShareGroup::GetTotalTextureUploadTime() {
+  DCHECK(texture_upload_stats_);
+  base::TimeDelta total_texture_upload_time;
+  texture_upload_stats_->GetStats(&total_texture_upload_time);
+  return total_texture_upload_time;
+}
+
+void AsyncPixelTransferDelegateShareGroup::ProcessMorePendingTransfers() {
+}
+
+bool AsyncPixelTransferDelegateShareGroup::NeedsProcessMorePendingTransfers() {
+  return false;
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h
new file mode 100644
index 0000000..f0887a9
--- /dev/null
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2013 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 GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_DELEGATE_SHARE_GROUP_H_
+#define GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_DELEGATE_SHARE_GROUP_H_
+
+#include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace gpu {
+
+class AsyncPixelTransferDelegateShareGroup : public AsyncPixelTransferDelegate {
+ public:
+  explicit AsyncPixelTransferDelegateShareGroup(gfx::GLContext* context);
+  virtual ~AsyncPixelTransferDelegateShareGroup();
+
+  // Implement AsyncPixelTransferDelegate:
+  virtual AsyncPixelTransferState* CreatePixelTransferState(
+      GLuint texture_id,
+      const AsyncTexImage2DParams& define_params) OVERRIDE;
+  virtual void BindCompletedAsyncTransfers() OVERRIDE;
+  virtual void AsyncNotifyCompletion(
+      const AsyncMemoryParams& mem_params,
+      const CompletionCallback& callback) OVERRIDE;
+  virtual void AsyncTexImage2D(
+      AsyncPixelTransferState* state,
+      const AsyncTexImage2DParams& tex_params,
+      const AsyncMemoryParams& mem_params,
+      const base::Closure& bind_callback) OVERRIDE;
+  virtual void AsyncTexSubImage2D(
+      AsyncPixelTransferState* state,
+      const AsyncTexSubImage2DParams& tex_params,
+      const AsyncMemoryParams& mem_params) OVERRIDE;
+  virtual void WaitForTransferCompletion(
+      AsyncPixelTransferState* state) OVERRIDE;
+  virtual uint32 GetTextureUploadCount() OVERRIDE;
+  virtual base::TimeDelta GetTotalTextureUploadTime() OVERRIDE;
+  virtual void ProcessMorePendingTransfers() OVERRIDE;
+  virtual bool NeedsProcessMorePendingTransfers() OVERRIDE;
+
+ private:
+  typedef std::list<base::WeakPtr<AsyncPixelTransferState> > TransferQueue;
+  TransferQueue pending_allocations_;
+
+  scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats_;
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateShareGroup);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_DELEGATE_SHARE_GROUP_H_
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index d1821d7..2a3809d 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -170,6 +170,7 @@
                                             feature_info_.get(),
                                             max_texture_size,
                                             max_cube_map_texture_size));
+  texture_manager_->set_framebuffer_manager(framebuffer_manager_.get());
 
   const GLint kMinTextureImageUnits = 8;
   const GLint kMinVertexTextureImageUnits = 0;
@@ -212,6 +213,20 @@
     return false;
   }
 
+  // TODO(gman): Use workarounds similar to max_texture_size above to implement.
+  if (gfx::GetGLImplementation() == gfx::kGLImplementationOSMesaGL) {
+    // Some shaders in Skia needed more than the min.
+    max_fragment_uniform_vectors_ =
+       std::min(static_cast<uint32>(kMinFragmentUniformVectors * 2),
+                max_fragment_uniform_vectors_);
+    max_varying_vectors_ =
+       std::min(static_cast<uint32>(kMinVaryingVectors * 2),
+                max_varying_vectors_);
+    max_vertex_uniform_vectors_ =
+       std::min(static_cast<uint32>(kMinVertexUniformVectors * 2),
+                max_vertex_uniform_vectors_);
+  }
+
   if (!texture_manager_->Initialize()) {
     LOG(ERROR) << "Context::Group::Initialize failed because texture manager "
                << "failed to initialize.";
@@ -251,6 +266,8 @@
 
   if (framebuffer_manager_ != NULL) {
     framebuffer_manager_->Destroy(have_context);
+    if (texture_manager_)
+      texture_manager_->set_framebuffer_manager(NULL);
     framebuffer_manager_.reset();
   }
 
diff --git a/gpu/command_buffer/service/context_state.h b/gpu/command_buffer/service/context_state.h
index af43eac..e2bc012 100644
--- a/gpu/command_buffer/service/context_state.h
+++ b/gpu/command_buffer/service/context_state.h
@@ -36,21 +36,21 @@
   GLenum bind_target;
 
   // texture currently bound to this unit's GL_TEXTURE_2D with glBindTexture
-  scoped_refptr<Texture> bound_texture_2d;
+  scoped_refptr<TextureRef> bound_texture_2d;
 
   // texture currently bound to this unit's GL_TEXTURE_CUBE_MAP with
   // glBindTexture
-  scoped_refptr<Texture> bound_texture_cube_map;
+  scoped_refptr<TextureRef> bound_texture_cube_map;
 
   // texture currently bound to this unit's GL_TEXTURE_EXTERNAL_OES with
   // glBindTexture
-  scoped_refptr<Texture> bound_texture_external_oes;
+  scoped_refptr<TextureRef> bound_texture_external_oes;
 
   // texture currently bound to this unit's GL_TEXTURE_RECTANGLE_ARB with
   // glBindTexture
-  scoped_refptr<Texture> bound_texture_rectangle_arb;
+  scoped_refptr<TextureRef> bound_texture_rectangle_arb;
 
-  scoped_refptr<Texture> GetInfoForSamplerType(
+  scoped_refptr<TextureRef> GetInfoForSamplerType(
       GLenum type) {
     DCHECK(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE ||
            type == GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_2D_RECT_ARB);
@@ -69,7 +69,7 @@
     return NULL;
   }
 
-  void Unbind(Texture* texture) {
+  void Unbind(TextureRef* texture) {
     if (bound_texture_2d == texture) {
       bound_texture_2d = NULL;
     }
diff --git a/gpu/command_buffer/service/error_state.cc b/gpu/command_buffer/service/error_state.cc
index 1e9148c..23fba49 100644
--- a/gpu/command_buffer/service/error_state.cc
+++ b/gpu/command_buffer/service/error_state.cc
@@ -113,7 +113,7 @@
     last_error_ = msg;
     logger_->LogMessage(
         filename, line,
-        logger_->GetLogPrefix() + ": " + std::string("GL ERROR :") +
+        std::string("GL ERROR :") +
         GLES2Util::GetStringEnum(error) + " : " +
         function_name + ": " + msg);
   }
@@ -170,7 +170,7 @@
       // GL_OUT_OF_MEMORY can legally happen on lost device.
       logger_->LogMessage(
           filename, line,
-          logger_->GetLogPrefix() + ": " + std::string("GL ERROR :") +
+          std::string("GL ERROR :") +
           GLES2Util::GetStringEnum(error) + " : " +
           function_name + ": was unhandled");
       NOTREACHED() << "GL error " << error << " was unhandled.";
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index fa8abdd..8b828e1 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -180,78 +180,6 @@
   StringSet extensions(
       reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)));
 
-  // This is a temporary fix to turn gl_tests green on Linux and Android bots.
-  // Once we migrate blacklisting stuff from src/content to src/gpu, we can
-  // get the workarounds from json file. Then we should remove this block.
-  // See crbug.com/228979.
-  bool is_intel = false;
-  bool is_nvidia = false;
-  bool is_amd = false;
-  bool is_mesa = false;
-  bool is_qualcomm = false;
-  bool is_imagination = false;
-  bool is_arm = false;
-  bool is_vivante = false;
-  const char* gl_strings[2];
-  gl_strings[0] = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
-  gl_strings[1] = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
-  if (!command_line.HasSwitch(switches::kGpuDriverBugWorkarounds) &&
-      !command_line.HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) {
-    for (size_t ii = 0; ii < arraysize(gl_strings); ++ii) {
-      const char* str = gl_strings[ii];
-      if (str) {
-        std::string lstr(StringToLowerASCII(std::string(str)));
-        StringSet string_set(lstr);
-        is_intel |= string_set.Contains("intel");
-        is_nvidia |= string_set.Contains("nvidia");
-        is_amd |= string_set.Contains("amd") || string_set.Contains("ati");
-        is_mesa |= string_set.Contains("mesa");
-        is_qualcomm |= string_set.Contains("qualcomm");
-        is_imagination |= string_set.Contains("imagination");
-        is_arm |= string_set.Contains("arm");
-      }
-    }
-    if (extensions.Contains("GL_VIV_shader_binary"))
-      is_vivante = true;
-
-    workarounds_.set_texture_filter_before_generating_mipmap = true;
-    workarounds_.clear_alpha_in_readpixels = true;
-    if (is_nvidia) {
-      workarounds_.use_current_program_after_successful_link = true;
-    }
-    if (is_qualcomm) {
-      workarounds_.restore_scissor_on_fbo_change = true;
-      workarounds_.flush_on_context_switch = true;
-      workarounds_.delete_instead_of_resize_fbo = true;
-    }
-    if (is_vivante || is_imagination) {
-      workarounds_.unbind_fbo_on_context_switch = true;
-    }
-#if defined(OS_MACOSX)
-    workarounds_.needs_offscreen_buffer_workaround = is_nvidia;
-    workarounds_.needs_glsl_built_in_function_emulation = is_amd;
-    if ((is_amd || is_intel) &&
-        gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL) {
-      workarounds_.reverse_point_sprite_coord_origin = true;
-    }
-    if (is_intel) {
-      workarounds_.max_texture_size = 4096;
-      workarounds_.max_cube_map_texture_size = 1024;
-      int32 major = 0;
-      int32 minor = 0;
-      int32 bugfix = 0;
-      base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
-      if (major < 10 ||
-          (major == 10 && ((minor == 7 && bugfix < 3) || (minor < 7))))
-        workarounds_.max_cube_map_texture_size = 512;
-    }
-    if (is_amd) {
-      workarounds_.max_texture_size = 4096;
-      workarounds_.max_cube_map_texture_size = 4096;
-    }
-#endif
-  }
-
   if (command_line.HasSwitch(switches::kGpuDriverBugWorkarounds)) {
     std::string types = command_line.GetSwitchValueASCII(
         switches::kGpuDriverBugWorkarounds);
@@ -282,8 +210,7 @@
   AddExtensionString("GL_CHROMIUM_texture_mailbox");
   AddExtensionString("GL_EXT_debug_marker");
 
-  if (workarounds_.enable_chromium_fast_npot_mo8_textures ||
-      is_imagination)
+  if (workarounds_.enable_chromium_fast_npot_mo8_textures)
     AddExtensionString("GL_CHROMIUM_fast_NPOT_MO8_textures");
 
   feature_flags_.chromium_stream_texture = true;
@@ -368,7 +295,7 @@
   // get rid of it.
   //
   bool enable_depth_texture = false;
-  if ((!workarounds_.disable_depth_texture && !is_qualcomm) &&
+  if (!workarounds_.disable_depth_texture &&
       (extensions.Contains("GL_ARB_depth_texture") ||
        extensions.Contains("GL_OES_depth_texture") ||
        extensions.Contains("GL_ANGLE_depth_texture"))) {
@@ -405,10 +332,6 @@
     feature_flags_.native_vertex_array_object = true;
   }
 
-  if (is_arm || is_imagination) {
-    workarounds_.use_client_side_arrays_for_stream_buffers = true;
-  }
-
   // If we're using client_side_arrays we have to emulate
   // vertex array objects since vertex array objects do not work
   // with client side arrays.
@@ -529,7 +452,7 @@
   // Check for multisample support
   bool ext_has_multisample =
       extensions.Contains("GL_EXT_framebuffer_multisample");
-  if (!is_qualcomm && !workarounds_.disable_angle_framebuffer_multisample) {
+  if (!workarounds_.disable_angle_framebuffer_multisample) {
     ext_has_multisample |=
        extensions.Contains("GL_ANGLE_framebuffer_multisample");
   }
@@ -548,8 +471,9 @@
     validators_.render_buffer_format.AddValue(GL_DEPTH_COMPONENT24);
   }
 
-  if (extensions.Contains("GL_OES_standard_derivatives") ||
-      gfx::HasDesktopGLFeatures()) {
+  if (!workarounds_.disable_oes_standard_derivatives &&
+      (extensions.Contains("GL_OES_standard_derivatives") ||
+       gfx::HasDesktopGLFeatures())) {
     AddExtensionString("GL_OES_standard_derivatives");
     feature_flags_.oes_standard_derivatives = true;
     validators_.hint_target.AddValue(GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES);
@@ -647,9 +571,6 @@
       extensions.Contains("GL_ARB_occlusion_query");
 
   if (!workarounds_.disable_ext_occlusion_query &&
-#if defined(OS_LINUX)
-      !is_intel &&
-#endif
       (have_ext_occlusion_query_boolean ||
        have_arb_occlusion_query2 ||
        have_arb_occlusion_query)) {
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index 3605976..ed0ec25 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -12,7 +12,7 @@
 #include "base/sys_info.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/gles2_cmd_validation.h"
-#include "gpu/command_buffer/service/gpu_driver_bug_workaround_type.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
 #include "gpu/gpu_export.h"
 
 class CommandLine;
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc
index 0fcc3a7..ac10e8d 100644
--- a/gpu/command_buffer/service/feature_info_unittest.cc
+++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -7,10 +7,10 @@
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_number_conversions.h"
-#include "gpu/command_buffer/service/gpu_driver_bug_workaround_type.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_mock.h"
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc
index 1ffd0fd..29fd55d 100644
--- a/gpu/command_buffer/service/framebuffer_manager.cc
+++ b/gpu/command_buffer/service/framebuffer_manager.cc
@@ -67,7 +67,7 @@
   }
 
   virtual bool IsTexture(
-      Texture* /* texture */) const OVERRIDE {
+      TextureRef* /* texture */) const OVERRIDE {
     return false;
   }
 
@@ -115,8 +115,8 @@
     : public Framebuffer::Attachment {
  public:
   TextureAttachment(
-      Texture* texture, GLenum target, GLint level)
-      : texture_(texture),
+      TextureRef* texture_ref, GLenum target, GLint level)
+      : texture_ref_(texture_ref),
         target_(target),
         level_(level) {
   }
@@ -124,21 +124,24 @@
   virtual GLsizei width() const OVERRIDE {
     GLsizei temp_width = 0;
     GLsizei temp_height = 0;
-    texture_->GetLevelSize(target_, level_, &temp_width, &temp_height);
+    texture_ref_->texture()->GetLevelSize(
+        target_, level_, &temp_width, &temp_height);
     return temp_width;
   }
 
   virtual GLsizei height() const OVERRIDE {
     GLsizei temp_width = 0;
     GLsizei temp_height = 0;
-    texture_->GetLevelSize(target_, level_, &temp_width, &temp_height);
+    texture_ref_->texture()->GetLevelSize(
+        target_, level_, &temp_width, &temp_height);
     return temp_height;
   }
 
   virtual GLenum internal_format() const OVERRIDE {
     GLenum temp_type = 0;
     GLenum temp_internal_format = 0;
-    texture_->GetLevelType(target_, level_, &temp_type, &temp_internal_format);
+    texture_ref_->texture()->GetLevelType(
+        target_, level_, &temp_type, &temp_internal_format);
     return temp_internal_format;
   }
 
@@ -147,18 +150,18 @@
   }
 
   virtual bool cleared() const OVERRIDE {
-    return texture_->IsLevelCleared(target_, level_);
+    return texture_ref_->texture()->IsLevelCleared(target_, level_);
   }
 
   virtual void SetCleared(
       RenderbufferManager* /* renderbuffer_manager */,
       TextureManager* texture_manager,
       bool cleared) OVERRIDE {
-    texture_manager->SetLevelCleared(texture_, target_, level_, cleared);
+    texture_manager->SetLevelCleared(texture_ref_, target_, level_, cleared);
   }
 
-  virtual bool IsTexture(Texture* texture) const OVERRIDE {
-    return texture == texture_.get();
+  virtual bool IsTexture(TextureRef* texture) const OVERRIDE {
+    return texture == texture_ref_.get();
   }
 
   virtual bool IsRenderbuffer(
@@ -167,23 +170,24 @@
     return false;
   }
 
-  Texture* texture() const {
-    return texture_.get();
+  TextureRef* texture() const {
+    return texture_ref_.get();
   }
 
   virtual bool CanRenderTo() const OVERRIDE {
-    return texture_->CanRenderTo();
+    return texture_ref_->texture()->CanRenderTo();
   }
 
   virtual void DetachFromFramebuffer() const OVERRIDE {
-    texture_->DetachFromFramebuffer();
+    texture_ref_->texture()->DetachFromFramebuffer();
   }
 
   virtual bool ValidForAttachmentType(
       GLenum attachment_type, uint32 max_color_attachments) OVERRIDE {
     GLenum type = 0;
     GLenum internal_format = 0;
-    if (!texture_->GetLevelType(target_, level_, &type, &internal_format)) {
+    if (!texture_ref_->texture()->GetLevelType(
+        target_, level_, &type, &internal_format)) {
       return false;
     }
     uint32 need = GLES2Util::GetChannelsNeededForAttachmentType(
@@ -195,14 +199,14 @@
   virtual void AddToSignature(
       TextureManager* texture_manager, std::string* signature) const OVERRIDE {
     DCHECK(signature);
-    texture_manager->AddToSignature(texture_, target_, level_, signature);
+    texture_manager->AddToSignature(texture_ref_, target_, level_, signature);
   }
 
  protected:
   virtual ~TextureAttachment() {}
 
  private:
-  scoped_refptr<Texture> texture_;
+  scoped_refptr<TextureRef> texture_ref_;
   GLenum target_;
   GLint level_;
 
@@ -466,14 +470,14 @@
 }
 
 void Framebuffer::UnbindTexture(
-    GLenum target, Texture* texture) {
+    GLenum target, TextureRef* texture_ref) {
   bool done;
   do {
     done = true;
     for (AttachmentMap::const_iterator it = attachments_.begin();
          it != attachments_.end(); ++it) {
       Attachment* attachment = it->second;
-      if (attachment->IsTexture(texture)) {
+      if (attachment->IsTexture(texture_ref)) {
         // TODO(gman): manually detach texture.
         // glFramebufferTexture2DEXT(target, it->first, GL_TEXTURE_2D, 0, 0);
         AttachTexture(it->first, NULL, GL_TEXTURE_2D, 0);
@@ -513,15 +517,15 @@
 }
 
 void Framebuffer::AttachTexture(
-    GLenum attachment, Texture* texture, GLenum target,
+    GLenum attachment, TextureRef* texture_ref, GLenum target,
     GLint level) {
   const Attachment* a = GetAttachment(attachment);
   if (a)
     a->DetachFromFramebuffer();
-  if (texture) {
+  if (texture_ref) {
     attachments_[attachment] = scoped_refptr<Attachment>(
-        new TextureAttachment(texture, target, level));
-    texture->AttachToFramebuffer();
+        new TextureAttachment(texture_ref, target, level));
+    texture_ref->texture()->AttachToFramebuffer();
   } else {
     attachments_.erase(attachment);
   }
diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h
index a5bdb3b..9b7b131 100644
--- a/gpu/command_buffer/service/framebuffer_manager.h
+++ b/gpu/command_buffer/service/framebuffer_manager.h
@@ -19,6 +19,7 @@
 class Renderbuffer;
 class RenderbufferManager;
 class Texture;
+class TextureRef;
 class TextureManager;
 
 // Info about a particular Framebuffer.
@@ -35,7 +36,7 @@
         RenderbufferManager* renderbuffer_manager,
         TextureManager* texture_manager,
         bool cleared) = 0;
-    virtual bool IsTexture(Texture* texture) const = 0;
+    virtual bool IsTexture(TextureRef* texture) const = 0;
     virtual bool IsRenderbuffer(
         Renderbuffer* renderbuffer) const = 0;
     virtual bool CanRenderTo() const = 0;
@@ -71,7 +72,7 @@
 
   // Attaches a texture to a particlar attachment. Pass null to detach.
   void AttachTexture(
-      GLenum attachment, Texture* texture, GLenum target,
+      GLenum attachment, TextureRef* texture_ref, GLenum target,
       GLint level);
 
   // Unbinds the given renderbuffer if it is bound.
@@ -80,7 +81,7 @@
 
   // Unbinds the given texture if it is bound.
   void UnbindTexture(
-      GLenum target, Texture* texture);
+      GLenum target, TextureRef* texture_ref);
 
   const Attachment* GetAttachment(GLenum attachment) const;
 
diff --git a/gpu/command_buffer/service/framebuffer_manager_unittest.cc b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
index b73a776..9a7f15b 100644
--- a/gpu/command_buffer/service/framebuffer_manager_unittest.cc
+++ b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
@@ -429,7 +429,7 @@
             framebuffer_->IsPossiblyComplete());
 
   texture_manager_.CreateTexture(kTextureClient1Id, kTextureService1Id);
-  scoped_refptr<Texture> texture1(
+  scoped_refptr<TextureRef> texture1(
       texture_manager_.GetTexture(kTextureClient1Id));
   ASSERT_TRUE(texture1 != NULL);
 
@@ -478,7 +478,7 @@
 
   // Check replacing an attachment
   texture_manager_.CreateTexture(kTextureClient2Id, kTextureService2Id);
-  scoped_refptr<Texture> texture2(
+  scoped_refptr<TextureRef> texture2(
       texture_manager_.GetTexture(kTextureClient2Id));
   ASSERT_TRUE(texture2 != NULL);
   texture_manager_.SetTarget(texture2, GL_TEXTURE_2D);
@@ -580,11 +580,11 @@
   const GLint kLevel1 = 0;
 
   texture_manager_.CreateTexture(kTextureClient1Id, kTextureService1Id);
-  scoped_refptr<Texture> texture1(
+  scoped_refptr<TextureRef> texture1(
       texture_manager_.GetTexture(kTextureClient1Id));
   ASSERT_TRUE(texture1 != NULL);
   texture_manager_.CreateTexture(kTextureClient2Id, kTextureService2Id);
-  scoped_refptr<Texture> texture2(
+  scoped_refptr<TextureRef> texture2(
       texture_manager_.GetTexture(kTextureClient2Id));
   ASSERT_TRUE(texture2 != NULL);
 
@@ -622,7 +622,7 @@
       renderbuffer_manager_.GetRenderbuffer(kRenderbufferClient1Id);
   ASSERT_TRUE(renderbuffer1 != NULL);
   texture_manager_.CreateTexture(kTextureClient2Id, kTextureService2Id);
-  scoped_refptr<Texture> texture2(
+  scoped_refptr<TextureRef> texture2(
       texture_manager_.GetTexture(kTextureClient2Id));
   ASSERT_TRUE(texture2 != NULL);
 
@@ -667,7 +667,7 @@
       renderbuffer_manager_.GetRenderbuffer(kRenderbufferClient1Id);
   ASSERT_TRUE(renderbuffer1 != NULL);
   texture_manager_.CreateTexture(kTextureClient2Id, kTextureService2Id);
-  scoped_refptr<Texture> texture2(
+  scoped_refptr<TextureRef> texture2(
       texture_manager_.GetTexture(kTextureClient2Id));
   ASSERT_TRUE(texture2 != NULL);
   texture_manager_.SetTarget(texture2, GL_TEXTURE_2D);
diff --git a/gpu/command_buffer/service/gl_context_virtual.cc b/gpu/command_buffer/service/gl_context_virtual.cc
index d142e01..8051e06 100644
--- a/gpu/command_buffer/service/gl_context_virtual.cc
+++ b/gpu/command_buffer/service/gl_context_virtual.cc
@@ -43,6 +43,7 @@
   }
 
   shared_context_->SetupForVirtualization();
+  shared_context_->MakeVirtuallyCurrent(this, compatible_surface);
   return true;
 }
 
@@ -53,9 +54,12 @@
 }
 
 bool GLContextVirtual::MakeCurrent(gfx::GLSurface* surface) {
-  if (decoder_.get() && decoder_->initialized())
+  // TODO(epenner): We should avoid bypassing MakeVirtuallyCurrent() below
+  // (return false or DCHECK when !decoder). To do this we must reorder
+  // tear-down in GpuCommandBufferStub::Destroy().
+  if (decoder_.get())
     shared_context_->MakeVirtuallyCurrent(this, surface);
-  else
+  else if (!IsCurrent(surface))
     shared_context_->MakeCurrent(surface);
   return true;
 }
diff --git a/gpu/command_buffer/service/gl_state_restorer_impl.cc b/gpu/command_buffer/service/gl_state_restorer_impl.cc
index d39b805..7b3c5ed 100644
--- a/gpu/command_buffer/service/gl_state_restorer_impl.cc
+++ b/gpu/command_buffer/service/gl_state_restorer_impl.cc
@@ -16,6 +16,11 @@
 GLStateRestorerImpl::~GLStateRestorerImpl() {
 }
 
+bool GLStateRestorerImpl::IsInitialized() {
+  DCHECK(decoder_.get());
+  return decoder_->initialized();
+}
+
 void GLStateRestorerImpl::RestoreState() {
   DCHECK(decoder_.get());
   decoder_->RestoreState();
diff --git a/gpu/command_buffer/service/gl_state_restorer_impl.h b/gpu/command_buffer/service/gl_state_restorer_impl.h
index aba3b1d..032bf72 100644
--- a/gpu/command_buffer/service/gl_state_restorer_impl.h
+++ b/gpu/command_buffer/service/gl_state_restorer_impl.h
@@ -23,6 +23,7 @@
    explicit GLStateRestorerImpl(base::WeakPtr<gles2::GLES2Decoder> decoder);
    virtual ~GLStateRestorerImpl();
 
+   virtual bool IsInitialized() OVERRIDE;
    virtual void RestoreState() OVERRIDE;
    virtual void RestoreAllTextureUnitBindings() OVERRIDE;
    virtual void RestoreFramebufferBindings() OVERRIDE;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 1637a0b..2fad005 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -561,7 +561,7 @@
   virtual void PerformIdleWork() OVERRIDE;
 
   virtual void SetResizeCallback(
-      const base::Callback<void(gfx::Size)>& callback) OVERRIDE;
+      const base::Callback<void(gfx::Size, float)>& callback) OVERRIDE;
 
   virtual Logger* GetLogger() OVERRIDE;
   virtual ErrorState* GetErrorState() OVERRIDE;
@@ -693,13 +693,13 @@
   }
 
   // Creates a Texture for the given texture.
-  Texture* CreateTexture(
+  TextureRef* CreateTexture(
       GLuint client_id, GLuint service_id) {
     return texture_manager()->CreateTexture(client_id, service_id);
   }
 
   // Gets the texture info for the given texture. Returns NULL if none exists.
-  Texture* GetTexture(GLuint client_id) const {
+  TextureRef* GetTexture(GLuint client_id) const {
     return texture_manager()->GetTexture(client_id);
   }
 
@@ -1376,9 +1376,9 @@
   }
 
   // Gets the texture id for a given target.
-  Texture* GetTextureInfoForTarget(GLenum target) {
+  TextureRef* GetTextureInfoForTarget(GLenum target) {
     TextureUnit& unit = state_.texture_units[state_.active_texture_unit];
-    Texture* texture = NULL;
+    TextureRef* texture = NULL;
     switch (target) {
       case GL_TEXTURE_2D:
         texture = unit.bound_texture_2d;
@@ -1405,9 +1405,9 @@
     return texture;
   }
 
-  Texture* GetTextureInfoForTargetUnlessDefault(
+  TextureRef* GetTextureInfoForTargetUnlessDefault(
       GLenum target) {
-    Texture* texture = GetTextureInfoForTarget(target);
+    TextureRef* texture = GetTextureInfoForTarget(target);
     if (!texture)
       return NULL;
     if (texture == texture_manager()->GetDefaultTextureInfo(target))
@@ -1614,7 +1614,7 @@
   // The copy that is saved when SwapBuffers is called.
   scoped_ptr<BackFramebuffer> offscreen_saved_frame_buffer_;
   scoped_ptr<BackTexture> offscreen_saved_color_texture_;
-  scoped_refptr<Texture>
+  scoped_refptr<TextureRef>
       offscreen_saved_color_texture_info_;
 
   // The copy that is used as the destination for multi-sample resolves.
@@ -1626,7 +1626,7 @@
 
   scoped_ptr<VertexArrayManager> vertex_array_manager_;
 
-  base::Callback<void(gfx::Size)> resize_callback_;
+  base::Callback<void(gfx::Size, float)> resize_callback_;
 
   WaitSyncPointCallback wait_sync_point_callback_;
 
@@ -2234,25 +2234,25 @@
   for (uint32 tt = 0; tt < state_.texture_units.size(); ++tt) {
     glActiveTexture(GL_TEXTURE0 + tt);
     // We want the last bind to be 2D.
-    Texture* texture;
+    TextureRef* ref;
     if (features().oes_egl_image_external) {
-      texture = texture_manager()->GetDefaultTextureInfo(
+      ref = texture_manager()->GetDefaultTextureInfo(
           GL_TEXTURE_EXTERNAL_OES);
-      state_.texture_units[tt].bound_texture_external_oes = texture;
-      glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture->service_id());
+      state_.texture_units[tt].bound_texture_external_oes = ref;
+      glBindTexture(GL_TEXTURE_EXTERNAL_OES, ref->service_id());
     }
     if (features().arb_texture_rectangle) {
-      texture = texture_manager()->GetDefaultTextureInfo(
+      ref = texture_manager()->GetDefaultTextureInfo(
           GL_TEXTURE_RECTANGLE_ARB);
-      state_.texture_units[tt].bound_texture_rectangle_arb = texture;
-      glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture->service_id());
+      state_.texture_units[tt].bound_texture_rectangle_arb = ref;
+      glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ref->service_id());
     }
-    texture = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_CUBE_MAP);
-    state_.texture_units[tt].bound_texture_cube_map = texture;
-    glBindTexture(GL_TEXTURE_CUBE_MAP, texture->service_id());
-    texture = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_2D);
-    state_.texture_units[tt].bound_texture_2d = texture;
-    glBindTexture(GL_TEXTURE_2D, texture->service_id());
+    ref = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_CUBE_MAP);
+    state_.texture_units[tt].bound_texture_cube_map = ref;
+    glBindTexture(GL_TEXTURE_CUBE_MAP, ref->service_id());
+    ref = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_2D);
+    state_.texture_units[tt].bound_texture_2d = ref;
+    glBindTexture(GL_TEXTURE_2D, ref->service_id());
   }
   glActiveTexture(GL_TEXTURE0);
   CHECK_GL_ERROR();
@@ -2504,9 +2504,11 @@
   resources.MaxFragmentUniformVectors =
       group_->max_fragment_uniform_vectors();
   resources.MaxDrawBuffers = group_->max_draw_buffers();
+  resources.MaxExpressionComplexity = 256;
+  resources.MaxCallStackDepth = 256;
 
 #if (ANGLE_SH_VERSION >= 110)
-  GLint range[2];
+  GLint range[2] = { 0, 0 };
   GLint precision = 0;
   GetShaderPrecisionFormatImpl(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT,
                                range, &precision);
@@ -2705,28 +2707,30 @@
   bool supports_separate_framebuffer_binds =
      features().chromium_framebuffer_multisample;
   for (GLsizei ii = 0; ii < n; ++ii) {
-    Texture* texture = GetTexture(client_ids[ii]);
-    if (texture && !texture->IsDeleted()) {
+    TextureRef* texture_ref = GetTexture(client_ids[ii]);
+    if (texture_ref) {
+      Texture* texture = texture_ref->texture();
       if (texture->IsAttachedToFramebuffer()) {
         clear_state_dirty_ = true;
       }
-      // Unbind texture from texture units.
+      // Unbind texture_ref from texture_ref units.
       for (size_t jj = 0; jj < state_.texture_units.size(); ++jj) {
-        state_.texture_units[jj].Unbind(texture);
+        state_.texture_units[jj].Unbind(texture_ref);
       }
       // Unbind from current framebuffers.
       if (supports_separate_framebuffer_binds) {
         if (state_.bound_read_framebuffer) {
           state_.bound_read_framebuffer->UnbindTexture(
-              GL_READ_FRAMEBUFFER_EXT, texture);
+              GL_READ_FRAMEBUFFER_EXT, texture_ref);
         }
         if (state_.bound_draw_framebuffer) {
           state_.bound_draw_framebuffer->UnbindTexture(
-              GL_DRAW_FRAMEBUFFER_EXT, texture);
+              GL_DRAW_FRAMEBUFFER_EXT, texture_ref);
         }
       } else {
         if (state_.bound_draw_framebuffer) {
-          state_.bound_draw_framebuffer->UnbindTexture(GL_FRAMEBUFFER, texture);
+          state_.bound_draw_framebuffer->UnbindTexture(GL_FRAMEBUFFER,
+                                                       texture_ref);
         }
       }
       GLuint service_id = texture->service_id();
@@ -2771,6 +2775,8 @@
   if (workarounds().unbind_fbo_on_context_switch)
     RestoreFramebufferBindings();
 
+  clear_state_dirty_ = true;
+
   return true;
 }
 
@@ -2972,9 +2978,9 @@
     // Update the info about the offscreen saved color texture in the parent.
     // The reference to the parent is a weak pointer and will become null if the
     // parent is later destroyed.
+    GLenum target = offscreen_saved_color_texture_info_->texture()->target();
     TextureManager* parent_texture_manager = parent_->texture_manager();
-    glBindTexture(offscreen_saved_color_texture_info_->target(),
-                  offscreen_saved_color_texture_info_->service_id());
+    glBindTexture(target, offscreen_saved_color_texture_info_->service_id());
     parent_texture_manager->SetLevelInfo(
         offscreen_saved_color_texture_info_,
         GL_TEXTURE_2D,
@@ -3011,16 +3017,15 @@
         offscreen_saved_color_texture_info_,
         GL_TEXTURE_WRAP_T,
         GL_CLAMP_TO_EDGE);
-    Texture* texture = GetTextureInfoForTarget(
-        offscreen_saved_color_texture_info_->target());
-    glBindTexture(texture->target(), texture->service_id());
+    TextureRef* texture_ref = GetTextureInfoForTarget(target);
+    glBindTexture(target, texture_ref ? texture_ref->service_id() : 0);
   } else {
     offscreen_saved_color_texture_info_ = NULL;
   }
 }
 
 void GLES2DecoderImpl::SetResizeCallback(
-    const base::Callback<void(gfx::Size)>& callback) {
+    const base::Callback<void(gfx::Size, float)>& callback) {
   resize_callback_ = callback;
 }
 
@@ -3058,9 +3063,9 @@
 
 bool GLES2DecoderImpl::GetServiceTextureId(uint32 client_texture_id,
                                            uint32* service_texture_id) {
-  Texture* texture = texture_manager()->GetTexture(client_texture_id);
-  if (texture) {
-    *service_texture_id = texture->service_id();
+  TextureRef* texture_ref = texture_manager()->GetTexture(client_texture_id);
+  if (texture_ref) {
+    *service_texture_id = texture_ref->service_id();
     return true;
   }
   return false;
@@ -3259,7 +3264,7 @@
 
     offscreen_saved_color_texture_info_ =
         new_parent_impl->CreateTexture(new_parent_texture_id, service_id);
-    offscreen_saved_color_texture_info_->SetNotOwned();
+    offscreen_saved_color_texture_info_->texture()->SetNotOwned();
     new_parent_impl->texture_manager()->
        SetTarget(offscreen_saved_color_texture_info_, GL_TEXTURE_2D);
 
@@ -3425,6 +3430,7 @@
 
   GLuint width = static_cast<GLuint>(c.width);
   GLuint height = static_cast<GLuint>(c.height);
+  GLfloat scale_factor = c.scale_factor;
   TRACE_EVENT2("gpu", "glResizeChromium", "width", width, "height", height);
 
   width = std::max(1U, width);
@@ -3445,7 +3451,7 @@
   }
 
   if (!resize_callback_.is_null()) {
-    resize_callback_.Run(gfx::Size(width, height));
+    resize_callback_.Run(gfx::Size(width, height), scale_factor);
     DCHECK(context_->IsCurrent(surface_.get()));
     if (!context_->IsCurrent(surface_.get())) {
       LOG(ERROR) << "GLES2DecoderImpl: Context lost because context no longer "
@@ -3672,6 +3678,8 @@
 }
 
 void GLES2DecoderImpl::RestoreState() const {
+  TRACE_EVENT1("gpu", "GLES2DecoderImpl::RestoreState",
+               "context", logger_.GetLogPrefix());
   // Restore the Framebuffer first because of bugs in Intel drivers.
   // Intel drivers incorrectly clip the viewport settings to
   // the size of the current framebuffer object.
@@ -3698,7 +3706,7 @@
 void GLES2DecoderImpl::RestoreTextureState(unsigned service_id) const {
   GLuint client_id = 0;
   if (texture_manager()->GetClientId(service_id, &client_id)) {
-    Texture* texture = GetTexture(client_id);
+    Texture* texture = GetTexture(client_id)->texture();
     GLenum target = texture->target();
     glBindTexture(target, service_id);
     glTexParameteri(
@@ -3811,11 +3819,11 @@
 }
 
 void GLES2DecoderImpl::DoBindTexture(GLenum target, GLuint client_id) {
-  Texture* texture = NULL;
+  TextureRef* texture_ref = NULL;
   GLuint service_id = 0;
   if (client_id != 0) {
-    texture = GetTexture(client_id);
-    if (!texture) {
+    texture_ref = GetTexture(client_id);
+    if (!texture_ref) {
       if (!group_->bind_generates_resource()) {
          LOG(ERROR) << "glBindTexture: id not generated by glGenTextures";
          current_decoder_error_ = error::kGenericError;
@@ -3826,14 +3834,15 @@
       glGenTextures(1, &service_id);
       DCHECK_NE(0u, service_id);
       CreateTexture(client_id, service_id);
-      texture = GetTexture(client_id);
+      texture_ref = GetTexture(client_id);
       IdAllocatorInterface* id_allocator =
           group_->GetIdAllocator(id_namespaces::kTextures);
       id_allocator->MarkAsUsed(client_id);
     }
   } else {
-    texture = texture_manager()->GetDefaultTextureInfo(target);
+    texture_ref = texture_manager()->GetDefaultTextureInfo(target);
   }
+  Texture* texture = texture_ref->texture();
 
   // Check the texture exists
   // Check that we are not trying to bind it to a different target.
@@ -3851,7 +3860,7 @@
   }
   LogClientServiceForInfo(texture, client_id, "glBindTexture");
   if (texture->target() == 0) {
-    texture_manager()->SetTarget(texture, target);
+    texture_manager()->SetTarget(texture_ref, target);
   }
   glBindTexture(target, texture->service_id());
 
@@ -3859,13 +3868,13 @@
   unit.bind_target = target;
   switch (target) {
     case GL_TEXTURE_2D:
-      unit.bound_texture_2d = texture;
+      unit.bound_texture_2d = texture_ref;
       break;
     case GL_TEXTURE_CUBE_MAP:
-      unit.bound_texture_cube_map = texture;
+      unit.bound_texture_cube_map = texture_ref;
       break;
     case GL_TEXTURE_EXTERNAL_OES:
-      unit.bound_texture_external_oes = texture;
+      unit.bound_texture_external_oes = texture_ref;
       if (texture->IsStreamTexture()) {
         DCHECK(stream_texture_manager_);
         StreamTexture* stream_tex =
@@ -3875,7 +3884,7 @@
       }
       break;
     case GL_TEXTURE_RECTANGLE_ARB:
-      unit.bound_texture_rectangle_arb = texture;
+      unit.bound_texture_rectangle_arb = texture_ref;
       break;
     default:
       NOTREACHED();  // Validation should prevent us getting here.
@@ -3952,9 +3961,9 @@
 }
 
 void GLES2DecoderImpl::DoGenerateMipmap(GLenum target) {
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture ||
-      !texture_manager()->CanGenerateMipmaps(texture)) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref ||
+      !texture_manager()->CanGenerateMipmaps(texture_ref)) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION, "glGenerateMipmap", "Can not generate mips");
     return;
@@ -3963,14 +3972,14 @@
   if (target == GL_TEXTURE_CUBE_MAP) {
     for (int i = 0; i < 6; ++i) {
       GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
-      if (!texture_manager()->ClearTextureLevel(this, texture, face, 0)) {
+      if (!texture_manager()->ClearTextureLevel(this, texture_ref, face, 0)) {
         LOCAL_SET_GL_ERROR(
             GL_OUT_OF_MEMORY, "glGenerateMipmap", "dimensions too big");
         return;
       }
     }
   } else {
-    if (!texture_manager()->ClearTextureLevel(this, texture, target, 0)) {
+    if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, 0)) {
       LOCAL_SET_GL_ERROR(
           GL_OUT_OF_MEMORY, "glGenerateMipmap", "dimensions too big");
       return;
@@ -3989,11 +3998,12 @@
   }
   glGenerateMipmapEXT(target);
   if (workarounds().set_texture_filter_before_generating_mipmap) {
-    glTexParameteri(target, GL_TEXTURE_MIN_FILTER, texture->min_filter());
+    glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
+                    texture_ref->texture()->min_filter());
   }
   GLenum error = LOCAL_PEEK_GL_ERROR("glGenerateMipmap");
   if (error == GL_NO_ERROR) {
-    texture_manager()->MarkMipmapsGenerated(texture);
+    texture_manager()->MarkMipmapsGenerated(texture_ref);
   }
 }
 
@@ -4882,16 +4892,16 @@
     return;
   }
   GLuint service_id = 0;
-  Texture* texture = NULL;
+  TextureRef* texture_ref = NULL;
   if (client_texture_id) {
-    texture = GetTexture(client_texture_id);
-    if (!texture) {
+    texture_ref = GetTexture(client_texture_id);
+    if (!texture_ref) {
       LOCAL_SET_GL_ERROR(
           GL_INVALID_OPERATION,
-          "glFramebufferTexture2D", "unknown texture");
+          "glFramebufferTexture2D", "unknown texture_ref");
       return;
     }
-    service_id = texture->service_id();
+    service_id = texture_ref->service_id();
   }
 
   if (!texture_manager()->ValidForTarget(textarget, level, 0, 0, 1)) {
@@ -4905,7 +4915,7 @@
   glFramebufferTexture2DEXT(target, attachment, textarget, service_id, level);
   GLenum error = LOCAL_PEEK_GL_ERROR("glFramebufferTexture2D");
   if (error == GL_NO_ERROR) {
-    framebuffer->AttachTexture(attachment, texture, textarget, level);
+    framebuffer->AttachTexture(attachment, texture_ref, textarget, level);
   }
   if (framebuffer == state_.bound_draw_framebuffer) {
     clear_state_dirty_ = true;
@@ -5146,7 +5156,7 @@
 
 void GLES2DecoderImpl::DoTexParameterf(
     GLenum target, GLenum pname, GLfloat param) {
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture = GetTextureInfoForTarget(target);
   if (!texture) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexParameterf", "unknown texture");
     return;
@@ -5159,7 +5169,7 @@
 
 void GLES2DecoderImpl::DoTexParameteri(
     GLenum target, GLenum pname, GLint param) {
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture = GetTextureInfoForTarget(target);
   if (!texture) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexParameteri", "unknown texture");
     return;
@@ -5171,7 +5181,7 @@
 
 void GLES2DecoderImpl::DoTexParameterfv(
     GLenum target, GLenum pname, const GLfloat* params) {
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture = GetTextureInfoForTarget(target);
   if (!texture) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexParameterfv", "unknown texture");
     return;
@@ -5184,7 +5194,7 @@
 
 void GLES2DecoderImpl::DoTexParameteriv(
   GLenum target, GLenum pname, const GLint* params) {
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture = GetTextureInfoForTarget(target);
   if (!texture) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_VALUE, "glTexParameteriv", "unknown texture");
@@ -5637,6 +5647,7 @@
   if (!texture_manager()->HaveUnrenderableTextures()) {
     return false;
   }
+  LOCAL_PERFORMANCE_WARNING("Some textures are unrenderable.");
   bool textures_set = false;
   const Program::SamplerIndices& sampler_indices =
      state_.current_program->sampler_indices();
@@ -5648,7 +5659,7 @@
       GLuint texture_unit_index = uniform_info->texture_units[jj];
       if (texture_unit_index < state_.texture_units.size()) {
         TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
-        Texture* texture =
+        TextureRef* texture =
             texture_unit.GetInfoForSamplerType(uniform_info->type);
         if (!texture || !texture_manager()->CanRender(texture)) {
           textures_set = true;
@@ -5682,17 +5693,17 @@
       GLuint texture_unit_index = uniform_info->texture_units[jj];
       if (texture_unit_index < state_.texture_units.size()) {
         TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
-        Texture* texture = uniform_info->type == GL_SAMPLER_2D ?
+        TextureRef* texture_ref = uniform_info->type == GL_SAMPLER_2D ?
             texture_unit.bound_texture_2d :
             texture_unit.bound_texture_cube_map;
-        if (!texture || !texture_manager()->CanRender(texture)) {
+        if (!texture_ref || !texture_manager()->CanRender(texture_ref)) {
           glActiveTexture(GL_TEXTURE0 + texture_unit_index);
-          // Get the texture info that was previously bound here.
-          texture = texture_unit.bind_target == GL_TEXTURE_2D ?
+          // Get the texture_ref info that was previously bound here.
+          texture_ref = texture_unit.bind_target == GL_TEXTURE_2D ?
               texture_unit.bound_texture_2d :
               texture_unit.bound_texture_cube_map;
           glBindTexture(texture_unit.bind_target,
-                        texture ? texture->service_id() : 0);
+                        texture_ref ? texture_ref->service_id() : 0);
         }
       }
     }
@@ -5719,10 +5730,10 @@
         GLuint texture_unit_index = uniform_info->texture_units[jj];
         if (texture_unit_index < state_.texture_units.size()) {
           TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
-          Texture* texture =
+          TextureRef* texture_ref =
               texture_unit.GetInfoForSamplerType(uniform_info->type);
-          if (texture && !texture->SafeToRenderFrom()) {
-            if (!texture_manager()->ClearRenderableLevels(this, texture)) {
+          if (texture_ref && !texture_ref->texture()->SafeToRenderFrom()) {
+            if (!texture_manager()->ClearRenderableLevels(this, texture_ref)) {
               return false;
             }
           }
@@ -6418,8 +6429,8 @@
 }
 
 bool GLES2DecoderImpl::DoIsTexture(GLuint client_id) {
-  const Texture* texture = GetTexture(client_id);
-  return texture && texture->IsValid() && !texture->IsDeleted();
+  const TextureRef* texture_ref = GetTexture(client_id);
+  return texture_ref && texture_ref->texture()->IsValid();
 }
 
 void GLES2DecoderImpl::DoAttachShader(
@@ -7336,7 +7347,7 @@
     }
     y += tile_height;
   }
-  Texture* texture = GetTextureInfoForTarget(bind_target);
+  TextureRef* texture = GetTextureInfoForTarget(bind_target);
   glBindTexture(bind_target, texture ? texture->service_id() : 0);
   return true;
 }
@@ -7511,13 +7522,14 @@
         "glCompressedTexImage2D", "dimensions out of range");
     return error::kNoError;
   }
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_VALUE,
         "glCompressedTexImage2D", "unknown texture target");
     return error::kNoError;
   }
+  Texture* texture = texture_ref->texture();
   if (texture->IsImmutable()) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
@@ -7540,9 +7552,6 @@
 
   if (texture->IsAttachedToFramebuffer()) {
     clear_state_dirty_ = true;
-    // TODO(gman): If textures tracked which framebuffers they were attached to
-    // we could just mark those framebuffers as not complete.
-    framebuffer_manager()->IncFramebufferStateChangeCount();
   }
 
   scoped_ptr<int8[]> zero;
@@ -7557,8 +7566,8 @@
   GLenum error = LOCAL_PEEK_GL_ERROR("glCompressedTexImage2D");
   if (error == GL_NO_ERROR) {
     texture_manager()->SetLevelInfo(
-        texture, target, level, internal_format, width, height, 1, border, 0, 0,
-        true);
+        texture_ref, target, level, internal_format,
+        width, height, 1, border, 0, 0, true);
   }
   return error::kNoError;
 }
@@ -7751,13 +7760,13 @@
         function_name, "can not supply data for depth or stencil textures");
     return false;
   }
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION, function_name, "unknown texture for target");
     return false;
   }
-  if (texture->IsImmutable()) {
+  if (texture_ref->texture()->IsImmutable()) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION, function_name, "texture is immutable");
     return false;
@@ -7786,7 +7795,8 @@
     return;
   }
 
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  Texture* texture = texture_ref->texture();
   GLsizei tex_width = 0;
   GLsizei tex_height = 0;
   GLenum tex_type = 0;
@@ -7800,7 +7810,7 @@
   if (level_is_same && !pixels) {
     // Just set the level texture but mark the texture as uncleared.
     texture_manager()->SetLevelInfo(
-        texture,
+        texture_ref,
         target, level, internal_format, width, height, 1, border, format, type,
         false);
     tex_image_2d_failed_ = false;
@@ -7809,14 +7819,11 @@
 
   if (texture->IsAttachedToFramebuffer()) {
     clear_state_dirty_ = true;
-    // TODO(gman): If textures tracked which framebuffers they were attached to
-    // we could just mark those framebuffers as not complete.
-    framebuffer_manager()->IncFramebufferStateChangeCount();
   }
 
   if (!teximage2d_faster_than_texsubimage2d_ && level_is_same && pixels) {
     glTexSubImage2D(target, level, 0, 0, width, height, format, type, pixels);
-    texture_manager()->SetLevelCleared(texture, target, level, true);
+    texture_manager()->SetLevelCleared(texture_ref, target, level, true);
     tex_image_2d_failed_ = false;
     return;
   }
@@ -7828,7 +7835,7 @@
   GLenum error = LOCAL_PEEK_GL_ERROR("glTexImage2D");
   if (error == GL_NO_ERROR) {
     texture_manager()->SetLevelInfo(
-        texture,
+        texture_ref,
         target, level, internal_format, width, height, 1, border, format, type,
         pixels != NULL);
     tex_image_2d_failed_ = false;
@@ -7908,13 +7915,14 @@
   GLenum format,
   GLsizei image_size,
   const void * data) {
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glCompressedTexSubImage2D", "unknown texture for target");
     return;
   }
+  Texture* texture = texture_ref->texture();
   GLenum type = 0;
   GLenum internal_format = 0;
   if (!texture->GetLevelType(target, level, &type, &internal_format)) {
@@ -7980,13 +7988,14 @@
     GLsizei height,
     GLint border) {
   DCHECK(!ShouldDeferReads());
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glCopyTexImage2D", "unknown texture for target");
     return;
   }
+  Texture* texture = texture_ref->texture();
   if (texture->IsImmutable()) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION, "glCopyTexImage2D", "texture is immutable");
@@ -8044,9 +8053,6 @@
 
   if (texture->IsAttachedToFramebuffer()) {
     clear_state_dirty_ = true;
-    // TODO(gman): If textures tracked which framebuffers they were attached to
-    // we could just mark those framebuffers as not complete.
-    framebuffer_manager()->IncFramebufferStateChangeCount();
   }
 
   // Clip to size to source dimensions
@@ -8086,7 +8092,7 @@
   GLenum error = LOCAL_PEEK_GL_ERROR("glCopyTexImage2D");
   if (error == GL_NO_ERROR) {
     texture_manager()->SetLevelInfo(
-        texture, target, level, internal_format, width, height, 1,
+        texture_ref, target, level, internal_format, width, height, 1,
         border, internal_format, GL_UNSIGNED_BYTE, true);
   }
 }
@@ -8101,13 +8107,14 @@
     GLsizei width,
     GLsizei height) {
   DCHECK(!ShouldDeferReads());
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glCopyTexSubImage2D", "unknown texture for target");
     return;
   }
+  Texture* texture = texture_ref->texture();
   GLenum type = 0;
   GLenum format = 0;
   if (!texture->GetLevelType(target, level, &type, &format) ||
@@ -8156,7 +8163,7 @@
   Clip(x, width, size.width(), &copyX, &copyWidth);
   Clip(y, height, size.height(), &copyY, &copyHeight);
 
-  if (!texture_manager()->ClearTextureLevel(this, texture, target, level)) {
+  if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, level)) {
     LOCAL_SET_GL_ERROR(
         GL_OUT_OF_MEMORY, "glCopyTexSubImage2D", "dimensions too big");
     return;
@@ -8226,13 +8233,14 @@
     LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, type, "type");
     return false;
   }
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         function_name, "unknown texture for target");
     return false;
   }
+  Texture* texture = texture_ref->texture();
   GLenum current_type = 0;
   GLenum internal_format = 0;
   if (!texture->GetLevelType(target, level, &current_type, &internal_format)) {
@@ -8292,14 +8300,16 @@
       xoffset, yoffset, width, height, format, type, data)) {
     return error;
   }
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  Texture* texture = texture_ref->texture();
   GLsizei tex_width = 0;
   GLsizei tex_height = 0;
   bool ok = texture->GetLevelSize(target, level, &tex_width, &tex_height);
   DCHECK(ok);
   if (xoffset != 0 || yoffset != 0 ||
       width != tex_width || height != tex_height) {
-    if (!texture_manager()->ClearTextureLevel(this, texture, target, level)) {
+    if (!texture_manager()->ClearTextureLevel(this, texture_ref,
+                                              target, level)) {
       LOCAL_SET_GL_ERROR(
           GL_OUT_OF_MEMORY, "glTexSubImage2D", "dimensions too big");
       return error::kNoError;
@@ -8321,7 +8331,7 @@
     glTexSubImage2D(
         target, level, xoffset, yoffset, width, height, format, type, data);
   }
-  texture_manager()->SetLevelCleared(texture, target, level, true);
+  texture_manager()->SetLevelCleared(texture_ref, target, level, true);
   return error::kNoError;
 }
 
@@ -8544,7 +8554,7 @@
 
   result->success = 1;  // true
 
-  GLint range[2] = {0, 0};
+  GLint range[2] = { 0, 0 };
   GLint precision = 0;
   GetShaderPrecisionFormatImpl(shader_type, precision_type, range, &precision);
 
@@ -8780,7 +8790,7 @@
       } else {
         // Flip the textures in the parent context via the texture manager.
         if (!!offscreen_saved_color_texture_info_.get())
-          offscreen_saved_color_texture_info_->
+          offscreen_saved_color_texture_info_->texture()->
               SetServiceId(offscreen_target_color_texture_->id());
 
         offscreen_saved_color_texture_.swap(offscreen_target_color_texture_);
@@ -9337,14 +9347,15 @@
   if (!result)
     return error::kOutOfBounds;
   *result = GL_ZERO;
-  Texture* texture = texture_manager()->GetTexture(client_id);
-  if (!texture) {
+  TextureRef* texture_ref = texture_manager()->GetTexture(client_id);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_VALUE,
         "glCreateStreamTextureCHROMIUM", "bad texture id.");
     return error::kNoError;
   }
 
+  Texture* texture = texture_ref->texture();
   if (texture->IsStreamTexture()) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
@@ -9367,7 +9378,7 @@
       texture->service_id(), client_id);
 
   if (object_id) {
-    texture->SetStreamTexture(true);
+    texture_manager()->SetStreamTexture(texture_ref, true);
   } else {
     LOCAL_SET_GL_ERROR(
         GL_OUT_OF_MEMORY,
@@ -9382,13 +9393,13 @@
     uint32 immediate_data_size,
     const cmds::DestroyStreamTextureCHROMIUM& c) {
   GLuint client_id = c.texture;
-  Texture* texture = texture_manager()->GetTexture(client_id);
-  if (texture && texture->IsStreamTexture()) {
+  TextureRef* texture_ref = texture_manager()->GetTexture(client_id);
+  if (texture_ref && texture_ref->texture()->IsStreamTexture()) {
     if (!stream_texture_manager_)
       return error::kInvalidArguments;
 
-    stream_texture_manager_->DestroyStreamTexture(texture->service_id());
-    texture->SetStreamTexture(false);
+    stream_texture_manager_->DestroyStreamTexture(texture_ref->service_id());
+    texture_manager()->SetStreamTexture(texture_ref, false);
   } else {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_VALUE,
@@ -9444,8 +9455,8 @@
 
   // Default target might be conceptually valid, but disallow it to avoid
   // accidents.
-  Texture* texture = GetTextureInfoForTargetUnlessDefault(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTargetUnlessDefault(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glTexImageIOSurface2DCHROMIUM", "no rectangle texture bound");
@@ -9466,11 +9477,11 @@
   }
 
   // Release any IOSurface previously bound to this texture.
-  ReleaseIOSurfaceForTexture(texture->service_id());
+  ReleaseIOSurfaceForTexture(texture_ref->service_id());
 
   // Make sure we release the IOSurface even if CGLTexImageIOSurface2D fails.
   texture_to_io_surface_map_.insert(
-      std::make_pair(texture->service_id(), surface));
+      std::make_pair(texture_ref->service_id(), surface));
 
   CGLContextObj context =
       static_cast<CGLContextObj>(context_->GetHandle());
@@ -9494,7 +9505,7 @@
   }
 
   texture_manager()->SetLevelInfo(
-      texture, target, 0, GL_RGBA, width, height, 1, 0,
+      texture_ref, target, 0, GL_RGBA, width, height, 1, 0,
       GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, true);
 
 #else
@@ -9551,10 +9562,10 @@
 void GLES2DecoderImpl::DoCopyTextureCHROMIUM(
     GLenum target, GLuint source_id, GLuint dest_id, GLint level,
     GLenum internal_format, GLenum dest_type) {
-  Texture* dest_texture = GetTexture(dest_id);
-  Texture* source_texture = GetTexture(source_id);
+  TextureRef* dest_texture_ref = GetTexture(dest_id);
+  TextureRef* source_texture_ref = GetTexture(source_id);
 
-  if (!source_texture || !dest_texture) {
+  if (!source_texture_ref || !dest_texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_VALUE, "glCopyTextureCHROMIUM", "unknown texture id");
     return;
@@ -9566,6 +9577,8 @@
     return;
   }
 
+  Texture* source_texture = source_texture_ref->texture();
+  Texture* dest_texture = dest_texture_ref->texture();
   if (dest_texture->target() != GL_TEXTURE_2D ||
       (source_texture->target() != GL_TEXTURE_2D &&
       source_texture->target() != GL_TEXTURE_EXTERNAL_OES)) {
@@ -9657,11 +9670,11 @@
     }
 
     texture_manager()->SetLevelInfo(
-        dest_texture, GL_TEXTURE_2D, level, internal_format, source_width,
+        dest_texture_ref, GL_TEXTURE_2D, level, internal_format, source_width,
         source_height, 1, 0, internal_format, dest_type, true);
   } else {
     texture_manager()->SetLevelCleared(
-        dest_texture, GL_TEXTURE_2D, level, true);
+        dest_texture_ref, GL_TEXTURE_2D, level, true);
   }
 
   // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix
@@ -9756,13 +9769,14 @@
         GL_INVALID_VALUE, "glTexStorage2DEXT", "dimensions out of range");
     return;
   }
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glTexStorage2DEXT", "unknown texture for target");
     return;
   }
+  Texture* texture = texture_ref->texture();
   if (texture->IsAttachedToFramebuffer()) {
     clear_state_dirty_ = true;
   }
@@ -9808,8 +9822,8 @@
     GLsizei level_height = height;
     for (int ii = 0; ii < levels; ++ii) {
       texture_manager()->SetLevelInfo(
-          texture, target, ii, format, level_width, level_height, 1, 0, format,
-          type, false);
+          texture_ref, target, ii, format,
+          level_width, level_height, 1, 0, format, type, false);
       level_width = std::max(1, level_width >> 1);
       level_height = std::max(1, level_height >> 1);
     }
@@ -9836,15 +9850,15 @@
       "context", logger_.GetLogPrefix(),
       "mailbox[0]", static_cast<unsigned char>(mailbox[0]));
 
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glProduceTextureCHROMIUM", "unknown texture for target");
     return;
   }
 
-  TextureDefinition* definition = texture_manager()->Save(texture);
+  TextureDefinition* definition = texture_manager()->Save(texture_ref);
   if (!definition) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
@@ -9858,7 +9872,7 @@
       definition,
       texture_manager())) {
     bool success = texture_manager()->Restore(
-        "glProductTextureCHROMIUM", this, texture, definition);
+        "glProductTextureCHROMIUM", this, texture_ref, definition);
     DCHECK(success);
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
@@ -9866,7 +9880,7 @@
     return;
   }
 
-  glBindTexture(texture->target(), texture->service_id());
+  glBindTexture(texture_ref->texture()->target(), texture_ref->service_id());
 }
 
 void GLES2DecoderImpl::DoConsumeTextureCHROMIUM(GLenum target,
@@ -9875,8 +9889,8 @@
       "context", logger_.GetLogPrefix(),
       "mailbox[0]", static_cast<unsigned char>(mailbox[0]));
 
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glConsumeTextureCHROMIUM", "unknown texture for target");
@@ -9895,7 +9909,7 @@
   }
 
   if (!texture_manager()->Restore(
-      "glConsumeTextureCHROMIUM", this, texture, definition.release())) {
+      "glConsumeTextureCHROMIUM", this, texture_ref, definition.release())) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glConsumeTextureCHROMIUM", "invalid texture");
@@ -9938,8 +9952,8 @@
 
   // Default target might be conceptually valid, but disallow it to avoid
   // accidents.
-  Texture* texture = GetTextureInfoForTargetUnlessDefault(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTargetUnlessDefault(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glBindTexImage2DCHROMIUM", "no texture bound");
@@ -9967,9 +9981,9 @@
 
   gfx::Size size = gl_image->GetSize();
   texture_manager()->SetLevelInfo(
-      texture, target, 0, GL_RGBA, size.width(), size.height(), 1, 0,
+      texture_ref, target, 0, GL_RGBA, size.width(), size.height(), 1, 0,
       GL_RGBA, GL_UNSIGNED_BYTE, true);
-  texture_manager()->SetLevelImage(texture, target, 0, gl_image);
+  texture_manager()->SetLevelImage(texture_ref, target, 0, gl_image);
 }
 
 void GLES2DecoderImpl::DoReleaseTexImage2DCHROMIUM(
@@ -9985,8 +9999,8 @@
 
   // Default target might be conceptually valid, but disallow it to avoid
   // accidents.
-  Texture* texture = GetTextureInfoForTargetUnlessDefault(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTargetUnlessDefault(target);
+  if (!texture_ref) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glReleaseTexImage2DCHROMIUM", "no texture bound");
@@ -10002,7 +10016,7 @@
   }
 
   // Do nothing when image is not currently bound.
-  if (texture->GetLevelImage(target, 0) != gl_image)
+  if (texture_ref->texture()->GetLevelImage(target, 0) != gl_image)
     return;
 
   {
@@ -10012,7 +10026,7 @@
   }
 
   texture_manager()->SetLevelInfo(
-      texture, target, 0, GL_RGBA, 0, 0, 1, 0,
+      texture_ref, target, 0, GL_RGBA, 0, 0, 1, 0,
       GL_RGBA, GL_UNSIGNED_BYTE, false);
 }
 
@@ -10159,7 +10173,8 @@
   }
 
   // Extra async validation.
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  Texture* texture = texture_ref->texture();
   if (!ValidateAsyncTransfer(
       "glAsyncTexImage2DCHROMIUM", texture, target, level, pixels))
     return error::kNoError;
@@ -10172,15 +10187,6 @@
     return error::kNoError;
   }
 
-  // Since we don't allow async redefinition, this is the only
-  // time the size of this texture can change while bound
-  // as a frame-buffer.
-  if (texture->IsAttachedToFramebuffer()) {
-    // TODO(gman): If textures tracked which framebuffers they were attached to
-    // we could just mark those framebuffers as not complete.
-    framebuffer_manager()->IncFramebufferStateChangeCount();
-  }
-
   if (!EnsureGPUMemoryAvailable(pixels_size)) {
     LOCAL_SET_GL_ERROR(
         GL_OUT_OF_MEMORY, "glAsyncTexImage2DCHROMIUM", "out of memory");
@@ -10206,7 +10212,7 @@
   // immutable so the async state stays valid. The level info
   // is set up lazily when the transfer completes.
   DCHECK(!texture->GetAsyncTransferState());
-  texture->SetAsyncTransferState(
+  texture_ref->SetAsyncTransferState(
       make_scoped_ptr(
           async_pixel_transfer_delegate_->CreatePixelTransferState(
               texture->service_id(),
@@ -10218,11 +10224,11 @@
       tex_params,
       mem_params,
       base::Bind(&TextureManager::SetLevelInfoFromParams,
-                 // The callback is only invoked if the transfer state
-                 // still exists, which implies through manager->info->state
+                 // The callback is only invoked if the transfer delegate still
+                 // exists, which implies through manager->texture_ref->state
                  // ownership that both of these pointers are valid.
                  base::Unretained(texture_manager()),
-                 base::Unretained(texture),
+                 base::Unretained(texture_ref),
                  tex_params));
   return error::kNoError;
 }
@@ -10258,7 +10264,8 @@
   }
 
   // Extra async validation.
-  Texture* texture = GetTextureInfoForTarget(target);
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  Texture* texture = texture_ref->texture();
   if (!ValidateAsyncTransfer(
          "glAsyncTexSubImage2DCHROMIUM", texture, target, level, pixels))
     return error::kNoError;
@@ -10270,7 +10277,8 @@
   // - Textures become immutable after an async call.
   // This way we know in all cases that an async texture is always clear.
   if (!texture->SafeToRenderFrom()) {
-    if (!texture_manager()->ClearTextureLevel(this, texture, target, level)) {
+    if (!texture_manager()->ClearTextureLevel(this, texture_ref,
+                                              target, level)) {
       LOCAL_SET_GL_ERROR(
           GL_OUT_OF_MEMORY,
           "glAsyncTexSubImage2DCHROMIUM", "dimensions too big");
@@ -10291,7 +10299,8 @@
                                               width, height, format, type};
   AsyncMemoryParams mem_params = {shared_memory, shm_size,
                                        shm_data_offset, shm_data_size};
-  if (!texture->GetAsyncTransferState()) {
+  AsyncPixelTransferState* state = texture->GetAsyncTransferState();
+  if (!state) {
     // TODO(epenner): We may want to enforce exclusive use
     // of async APIs in which case this should become an error,
     // (the texture should have been async defined).
@@ -10303,16 +10312,15 @@
                                          &define_params.internal_format);
     // Set up the async state if needed, and make the texture
     // immutable so the async state stays valid.
-    texture->SetAsyncTransferState(
-        make_scoped_ptr(
-            async_pixel_transfer_delegate_->CreatePixelTransferState(
-                texture->service_id(),
-                define_params)));
+    state = async_pixel_transfer_delegate_->CreatePixelTransferState(
+        texture->service_id(),
+        define_params);
+    texture_ref->SetAsyncTransferState(make_scoped_ptr(state));
     texture->SetImmutable(true);
   }
 
   async_pixel_transfer_delegate_->AsyncTexSubImage2D(
-      texture->GetAsyncTransferState(), tex_params, mem_params);
+      state, tex_params, mem_params);
   return error::kNoError;
 }
 
@@ -10326,15 +10334,22 @@
         GL_INVALID_ENUM, "glWaitAsyncTexImage2DCHROMIUM", "target");
     return error::kNoError;
   }
-  Texture* texture = GetTextureInfoForTarget(target);
-  if (!texture) {
+  TextureRef* texture_ref = GetTextureInfoForTarget(target);
+  if (!texture_ref) {
       LOCAL_SET_GL_ERROR(
           GL_INVALID_OPERATION,
           "glWaitAsyncTexImage2DCHROMIUM", "unknown texture");
     return error::kNoError;
   }
-  async_pixel_transfer_delegate_->WaitForTransferCompletion(
-      texture->GetAsyncTransferState());
+  AsyncPixelTransferState* state =
+      texture_ref->texture()->GetAsyncTransferState();
+  if (!state) {
+      LOCAL_SET_GL_ERROR(
+          GL_INVALID_OPERATION,
+          "glWaitAsyncTexImage2DCHROMIUM", "No async transfer started");
+    return error::kNoError;
+  }
+  async_pixel_transfer_delegate_->WaitForTransferCompletion(state);
   ProcessFinishedAsyncTransfers();
   return error::kNoError;
 }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 14c82f0..86d4801 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -173,7 +173,7 @@
   // Sets a callback which is called when a glResizeCHROMIUM command
   // is processed.
   virtual void SetResizeCallback(
-      const base::Callback<void(gfx::Size)>& callback) = 0;
+      const base::Callback<void(gfx::Size, float)>& callback) = 0;
 
   virtual void SetStreamTextureManager(StreamTextureManager* manager) = 0;
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index 12d1cbd..3f714bd 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -68,7 +68,8 @@
   MOCK_CONST_METHOD1(RestoreTextureUnitBindings, void(unsigned unit));
   MOCK_METHOD0(GetQueryManager, gpu::gles2::QueryManager*());
   MOCK_METHOD0(GetVertexArrayManager, gpu::gles2::VertexArrayManager*());
-  MOCK_METHOD1(SetResizeCallback, void(const base::Callback<void(gfx::Size)>&));
+  MOCK_METHOD1(
+      SetResizeCallback, void(const base::Callback<void(gfx::Size, float)>&));
   MOCK_METHOD1(SetStreamTextureManager, void(StreamTextureManager*));
   MOCK_METHOD0(GetAsyncPixelTransferDelegate,
       AsyncPixelTransferDelegate*());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 2292b8b..cd62780 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -1822,8 +1822,9 @@
 TEST_F(GLES2DecoderTest, GenerateMipmapHandlesOutOfMemory) {
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
   TextureManager* manager = group().texture_manager();
-  Texture* texture = manager->GetTexture(client_texture_id_);
-  ASSERT_TRUE(texture != NULL);
+  TextureRef* texture_ref = manager->GetTexture(client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   GLint width = 0;
   GLint height = 0;
   EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 2, &width, &height));
@@ -4726,8 +4727,9 @@
   GLenum type = GL_UNSIGNED_BYTE;
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
   TextureManager* manager = group().texture_manager();
-  Texture* texture = manager->GetTexture(client_texture_id_);
-  ASSERT_TRUE(texture != NULL);
+  TextureRef* texture_ref = manager->GetTexture(client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, level, &width, &height));
   EXPECT_CALL(*gl_, GetError())
       .WillOnce(Return(GL_NO_ERROR))
@@ -4776,8 +4778,9 @@
   GLint border = 0;
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
   TextureManager* manager = group().texture_manager();
-  Texture* texture = manager->GetTexture(client_texture_id_);
-  ASSERT_TRUE(texture != NULL);
+  TextureRef* texture_ref = manager->GetTexture(client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, level, &width, &height));
   EXPECT_CALL(*gl_, GetError())
       .WillOnce(Return(GL_NO_ERROR))
@@ -5307,8 +5310,9 @@
   EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
 
   // Test TexSubImage not allowed for ETC1 compressed texture
-  Texture* texture = GetTexture(client_texture_id_);
-  ASSERT_TRUE(texture != NULL);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   GLenum type, internal_format;
   EXPECT_TRUE(texture->GetLevelType(GL_TEXTURE_2D, 0, &type, &internal_format));
   EXPECT_EQ(kFormat, internal_format);
@@ -5417,9 +5421,9 @@
   cmd.Init(GL_TEXTURE_EXTERNAL_OES, kNewClientId);
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
-  Texture* texture = GetTexture(kNewClientId);
-  EXPECT_TRUE(texture != NULL);
-  EXPECT_TRUE(texture->target() == GL_TEXTURE_EXTERNAL_OES);
+  TextureRef* texture_ref = GetTexture(kNewClientId);
+  EXPECT_TRUE(texture_ref != NULL);
+  EXPECT_TRUE(texture_ref->texture()->target() == GL_TEXTURE_EXTERNAL_OES);
 }
 
 TEST_F(GLES2DecoderManualInitTest, EGLImageExternalGetBinding) {
@@ -5467,8 +5471,9 @@
       true);   // bind generates resource
   DoBindTexture(GL_TEXTURE_EXTERNAL_OES, client_texture_id_, kServiceTextureId);
 
-  Texture* texture = GetTexture(client_texture_id_);
-  EXPECT_TRUE(texture != NULL);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  EXPECT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_TRUE(texture->target() == GL_TEXTURE_EXTERNAL_OES);
   EXPECT_TRUE(texture->min_filter() == GL_LINEAR);
   EXPECT_TRUE(texture->wrap_s() == GL_CLAMP_TO_EDGE);
@@ -5525,8 +5530,9 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 
-  Texture* texture = GetTexture(client_texture_id_);
-  EXPECT_TRUE(texture != NULL);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  EXPECT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_TRUE(texture->target() == GL_TEXTURE_EXTERNAL_OES);
   EXPECT_TRUE(texture->min_filter() == GL_LINEAR);
   EXPECT_TRUE(texture->wrap_s() == GL_CLAMP_TO_EDGE);
@@ -5565,8 +5571,9 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
 
-  Texture* texture = GetTexture(client_texture_id_);
-  EXPECT_TRUE(texture != NULL);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  EXPECT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_TRUE(texture->target() == GL_TEXTURE_EXTERNAL_OES);
   EXPECT_TRUE(texture->min_filter() == GL_LINEAR);
   EXPECT_TRUE(texture->wrap_s() == GL_CLAMP_TO_EDGE);
@@ -5658,9 +5665,9 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(kObjectId, *result);
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
-  Texture* texture = GetTexture(client_texture_id_);
-  EXPECT_TRUE(texture != NULL);
-  EXPECT_TRUE(texture->IsStreamTexture());
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  EXPECT_TRUE(texture_ref != NULL);
+  EXPECT_TRUE(texture_ref->texture()->IsStreamTexture());
 }
 
 TEST_F(GLES2DecoderManualInitTest, CreateStreamTextureCHROMIUMBadId) {
@@ -5715,8 +5722,8 @@
       false,   // request stencil
       true);   // bind generates resource
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(true);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, true);
 
   CreateStreamTextureCHROMIUM cmd;
   cmd.Init(client_texture_id_, shared_memory_id_, shared_memory_offset_);
@@ -5739,8 +5746,8 @@
   StrictMock<MockStreamTexture> stream_texture;
   decoder_->SetStreamTextureManager(&manager);
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(true);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, true);
 
   EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_EXTERNAL_OES, kServiceTextureId))
       .Times(1)
@@ -5769,8 +5776,8 @@
       false,   // request stencil
       true);   // bind generates resource
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(true);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, true);
 
   BindTexture cmd;
   cmd.Init(GL_TEXTURE_2D, client_texture_id_);
@@ -5797,8 +5804,8 @@
   StrictMock<MockStreamTextureManager> manager;
   decoder_->SetStreamTextureManager(&manager);
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(true);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, true);
 
   EXPECT_CALL(manager, DestroyStreamTexture(kServiceTextureId))
       .Times(1)
@@ -5808,8 +5815,8 @@
   cmd.Init(client_texture_id_);
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
-  EXPECT_FALSE(texture->IsStreamTexture());
-  EXPECT_EQ(0U, texture->target());
+  EXPECT_FALSE(texture_ref->texture()->IsStreamTexture());
+  EXPECT_EQ(0U, texture_ref->texture()->target());
 }
 
 TEST_F(GLES2DecoderManualInitTest, DestroyStreamTextureCHROMIUMInvalid) {
@@ -5823,8 +5830,8 @@
       false,   // request stencil
       true);   // bind generates resource
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(false);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, false);
 
   DestroyStreamTextureCHROMIUM cmd;
   cmd.Init(client_texture_id_);
@@ -5865,8 +5872,8 @@
   EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd));
   GetGLError(); // ignore internal error
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(true);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, true);
 
   DestroyStreamTextureCHROMIUM cmd2;
   cmd2.Init(client_texture_id_);
@@ -5904,8 +5911,8 @@
       .WillOnce(Return(kObjectId))
       .RetiresOnSaturation();
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(true);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, true);
 
   DoBindTexture(GL_TEXTURE_EXTERNAL_OES, client_texture_id_, kServiceTextureId);
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
@@ -5914,13 +5921,13 @@
   cmd.Init(client_texture_id_);
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
-  EXPECT_FALSE(texture->IsStreamTexture());
+  EXPECT_FALSE(texture_ref->texture()->IsStreamTexture());
 
   CreateStreamTextureCHROMIUM cmd2;
   cmd2.Init(client_texture_id_, shared_memory_id_, shared_memory_offset_);
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
-  EXPECT_TRUE(texture->IsStreamTexture());
+  EXPECT_TRUE(texture_ref->texture()->IsStreamTexture());
 }
 
 TEST_F(GLES2DecoderManualInitTest, ProduceAndConsumeStreamTextureCHROMIUM) {
@@ -5938,8 +5945,8 @@
   StrictMock<MockStreamTexture> stream_texture;
   decoder_->SetStreamTextureManager(&manager);
 
-  Texture* texture = GetTexture(client_texture_id_);
-  texture->SetStreamTexture(true);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  group().texture_manager()->SetStreamTexture(texture_ref, true);
 
   EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_EXTERNAL_OES, kServiceTextureId))
       .Times(1)
@@ -5962,7 +5969,7 @@
 
   memcpy(shared_memory_address_, mailbox, sizeof(mailbox));
 
-  EXPECT_EQ(kServiceTextureId, texture->service_id());
+  EXPECT_EQ(kServiceTextureId, texture_ref->service_id());
 
   // Assigns and binds new service side texture ID.
   EXPECT_CALL(*gl_, GenTextures(1, _))
@@ -6019,7 +6026,7 @@
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 
   // Service ID is restored.
-  EXPECT_EQ(kServiceTextureId, texture->service_id());
+  EXPECT_EQ(kServiceTextureId, texture_ref->service_id());
 }
 
 TEST_F(GLES2DecoderManualInitTest, ARBTextureRectangleBindTexture) {
@@ -6039,7 +6046,7 @@
   cmd.Init(GL_TEXTURE_RECTANGLE_ARB, kNewClientId);
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
-  Texture* texture = GetTexture(kNewClientId);
+  Texture* texture = GetTexture(kNewClientId)->texture();
   EXPECT_TRUE(texture != NULL);
   EXPECT_TRUE(texture->target() == GL_TEXTURE_RECTANGLE_ARB);
 }
@@ -6091,7 +6098,7 @@
   DoBindTexture(
       GL_TEXTURE_RECTANGLE_ARB, client_texture_id_, kServiceTextureId);
 
-  Texture* texture = GetTexture(client_texture_id_);
+  Texture* texture = GetTexture(client_texture_id_)->texture();
   EXPECT_TRUE(texture != NULL);
   EXPECT_TRUE(texture->target() == GL_TEXTURE_RECTANGLE_ARB);
   EXPECT_TRUE(texture->min_filter() == GL_LINEAR);
@@ -6150,7 +6157,7 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 
-  Texture* texture = GetTexture(client_texture_id_);
+  Texture* texture = GetTexture(client_texture_id_)->texture();
   EXPECT_TRUE(texture != NULL);
   EXPECT_TRUE(texture->target() == GL_TEXTURE_RECTANGLE_ARB);
   EXPECT_TRUE(texture->min_filter() == GL_LINEAR);
@@ -6191,7 +6198,7 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
 
-  Texture* texture = GetTexture(client_texture_id_);
+  Texture* texture = GetTexture(client_texture_id_)->texture();
   EXPECT_TRUE(texture != NULL);
   EXPECT_TRUE(texture->target() == GL_TEXTURE_RECTANGLE_ARB);
   EXPECT_TRUE(texture->min_filter() == GL_LINEAR);
@@ -6522,7 +6529,9 @@
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
 
   TextureManager* manager = group().texture_manager();
-  Texture* texture = manager->GetTexture(client_texture_id_);
+  TextureRef* texture_ref = manager->GetTexture(client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
 
   EXPECT_CALL(*gl_, GetError())
       .WillOnce(Return(GL_NO_ERROR))
@@ -6583,8 +6592,8 @@
            8, kSharedMemoryId, kSharedMemoryOffset);
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   TextureManager* manager = group().texture_manager();
-  Texture* texture = manager->GetTexture(client_texture_id_);
-  EXPECT_TRUE(texture->SafeToRenderFrom());
+  TextureRef* texture_ref = manager->GetTexture(client_texture_id_);
+  EXPECT_TRUE(texture_ref->texture()->SafeToRenderFrom());
 }
 
 TEST_F(GLES2DecoderWithShaderTest, UnClearedAttachmentsGetClearedOnClear) {
@@ -7390,7 +7399,10 @@
                0, 0);
   DoTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                0, 0);
-  Texture* texture = group().texture_manager()->GetTexture(client_texture_id_);
+  TextureRef* texture_ref = group().texture_manager()->GetTexture(
+      client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(kServiceTextureId, texture->service_id());
 
   // Assigns and binds new service side texture ID.
@@ -7992,7 +8004,10 @@
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
   DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                0, 0);
-  Texture* texture = group().texture_manager()->GetTexture(client_texture_id_);
+  TextureRef* texture_ref = group().texture_manager()->GetTexture(
+      client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(kServiceTextureId, texture->service_id());
 
   group().image_manager()->AddImage(gfx::GLImage::CreateGLImage(0), 1);
@@ -8036,7 +8051,10 @@
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
   DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                0, 0);
-  Texture* texture = group().texture_manager()->GetTexture(client_texture_id_);
+  TextureRef* texture_ref = group().texture_manager()->GetTexture(
+      client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(kServiceTextureId, texture->service_id());
 
   group().image_manager()->AddImage(gfx::GLImage::CreateGLImage(0), 1);
@@ -8093,7 +8111,7 @@
       false,   // request stencil
       true);   // bind generates resource
 
-  Texture* texture = GetTexture(client_texture_id_);
+  Texture* texture = GetTexture(client_texture_id_)->texture();
   EXPECT_TRUE(texture != NULL);
   EXPECT_TRUE(texture->pool() == GL_TEXTURE_POOL_UNMANAGED_CHROMIUM);
 
@@ -8131,7 +8149,8 @@
 
   // Set up the texture.
   DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
-  Texture* texture = GetTexture(client_texture_id_);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+  Texture* texture = texture_ref->texture();
 
   // Set a mock Async delegate
   StrictMock<gpu::MockAsyncPixelTransferDelegate>* delegate =
@@ -8203,7 +8222,7 @@
   }
 
   // AsyncTexSubImage2D
-  texture->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
+  texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
   texture->SetImmutable(false);
   {
     // Create transfer state since it doesn't exist.
@@ -8254,11 +8273,12 @@
     // asynchronously and AsyncTexSubImage2D does not involved binding.
     EXPECT_CALL(*gl_, GenTextures(1, _))
         .WillOnce(SetArgumentPointee<1>(kServiceTextureId));
-    texture->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
+    texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
     DoDeleteTexture(client_texture_id_, kServiceTextureId);
     DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
-    texture = GetTexture(client_texture_id_);
-    texture->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
+    texture_ref = GetTexture(client_texture_id_);
+    texture = texture_ref->texture();
+    texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
     texture->SetImmutable(false);
     // Create transfer state since it doesn't exist.
     EXPECT_CALL(*delegate, CreatePixelTransferState(kServiceTextureId, _))
@@ -8280,7 +8300,7 @@
   }
 
   decoder_->SetAsyncPixelTransferDelegate(NULL);
-  texture->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
+  texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
 }
 
 namespace {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index c4db9f2..55dc7e4 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -111,7 +111,7 @@
     return group_->renderbuffer_manager()->GetRenderbuffer(service_id);
   }
 
-  Texture* GetTexture(GLuint client_id) {
+  TextureRef* GetTexture(GLuint client_id) {
     return group_->texture_manager()->GetTexture(client_id);
   }
 
diff --git a/gpu/command_buffer/service/gpu_scheduler.cc b/gpu/command_buffer/service/gpu_scheduler.cc
index 82f22c5..24de4d2 100644
--- a/gpu/command_buffer/service/gpu_scheduler.cc
+++ b/gpu/command_buffer/service/gpu_scheduler.cc
@@ -45,7 +45,9 @@
 }
 
 void GpuScheduler::PutChanged() {
-  TRACE_EVENT1("gpu", "GpuScheduler:PutChanged", "this", this);
+  TRACE_EVENT1(
+     "gpu", "GpuScheduler:PutChanged",
+     "decoder", decoder_ ? decoder_->GetLogger()->GetLogPrefix() : "None");
 
   CommandBuffer::State state = command_buffer_->GetState();
 
diff --git a/gpu/command_buffer/service/gpu_switches.cc b/gpu/command_buffer/service/gpu_switches.cc
index 6d699ad..221dd60 100644
--- a/gpu/command_buffer/service/gpu_switches.cc
+++ b/gpu/command_buffer/service/gpu_switches.cc
@@ -62,6 +62,10 @@
 // Disables the GPU shader on disk cache.
 const char kDisableGpuShaderDiskCache[]     = "disable-gpu-shader-disk-cache";
 
+// Allows async texture uploads (off main thread) via GL context sharing.
+const char kEnableShareGroupAsyncTextureUpload[] =
+    "enable-share-group-async-texture-upload";
+
 const char* kGpuSwitches[] = {
   kCompileShaderAlwaysSucceeds,
   kDisableGLErrorLimit,
@@ -80,6 +84,7 @@
   kGpuProgramCacheSizeKb,
   kTraceGL,
   kDisableGpuShaderDiskCache,
+  kEnableShareGroupAsyncTextureUpload,
 };
 
 const int kNumGpuSwitches = arraysize(kGpuSwitches);
diff --git a/gpu/command_buffer/service/gpu_switches.h b/gpu/command_buffer/service/gpu_switches.h
index ba6f804..7634c57 100644
--- a/gpu/command_buffer/service/gpu_switches.h
+++ b/gpu/command_buffer/service/gpu_switches.h
@@ -28,6 +28,7 @@
 GPU_EXPORT extern const char kGpuProgramCacheSizeKb[];
 GPU_EXPORT extern const char kTraceGL[];
 GPU_EXPORT extern const char kDisableGpuShaderDiskCache[];
+GPU_EXPORT extern const char kEnableShareGroupAsyncTextureUpload[];
 
 GPU_EXPORT extern const char* kGpuSwitches[];
 GPU_EXPORT extern const int kNumGpuSwitches;
diff --git a/gpu/command_buffer/service/logger.cc b/gpu/command_buffer/service/logger.cc
index 54b0233..b985e5e 100644
--- a/gpu/command_buffer/service/logger.cc
+++ b/gpu/command_buffer/service/logger.cc
@@ -18,7 +18,8 @@
       log_message_count_(0),
       log_synthesized_gl_errors_(true) {
   Logger* this_temp = this;
-  this_in_hex_ = base::HexEncode(&this_temp, sizeof(this_temp));
+  this_in_hex_ = std::string("GroupMarkerNotSet(crbug.com/242999)!:") +
+      base::HexEncode(&this_temp, sizeof(this_temp));
 }
 
 Logger::~Logger() {}
@@ -28,15 +29,16 @@
   if (log_message_count_ < kMaxLogMessages ||
       CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableGLErrorLimit)) {
+    std::string prefixed_msg(std::string("[") + GetLogPrefix() + "]" + msg);
     ++log_message_count_;
     // LOG this unless logging is turned off as any chromium code that
     // generates these errors probably has a bug.
     if (log_synthesized_gl_errors_) {
       ::logging::LogMessage(
-          filename, line, ::logging::LOG_ERROR).stream() << msg;
+          filename, line, ::logging::LOG_ERROR).stream() << prefixed_msg;
     }
     if (!msg_callback_.is_null()) {
-      msg_callback_.Run(0, msg);
+      msg_callback_.Run(0, prefixed_msg);
     }
   } else {
     if (log_message_count_ == kMaxLogMessages) {
diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc
index d9d944d..2582a49 100644
--- a/gpu/command_buffer/service/memory_program_cache.cc
+++ b/gpu/command_buffer/service/memory_program_cache.cc
@@ -107,13 +107,17 @@
 ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram(
     GLuint program,
     Shader* shader_a,
+    const ShaderTranslatorInterface* translator_a,
     Shader* shader_b,
+    const ShaderTranslatorInterface* translator_b,
     const LocationMap* bind_attrib_location_map,
     const ShaderCacheCallback& shader_callback) const {
   char a_sha[kHashLength];
   char b_sha[kHashLength];
-  ComputeShaderHash(*shader_a->deferred_compilation_source(), a_sha);
-  ComputeShaderHash(*shader_b->deferred_compilation_source(), b_sha);
+  ComputeShaderHash(
+      *shader_a->deferred_compilation_source(), translator_a, a_sha);
+  ComputeShaderHash(
+      *shader_b->deferred_compilation_source(), translator_b, b_sha);
 
   char sha[kHashLength];
   ComputeProgramHash(a_sha,
@@ -160,7 +164,9 @@
 void MemoryProgramCache::SaveLinkedProgram(
     GLuint program,
     const Shader* shader_a,
+    const ShaderTranslatorInterface* translator_a,
     const Shader* shader_b,
+    const ShaderTranslatorInterface* translator_b,
     const LocationMap* bind_attrib_location_map,
     const ShaderCacheCallback& shader_callback) {
   GLenum format;
@@ -179,8 +185,10 @@
 
   char a_sha[kHashLength];
   char b_sha[kHashLength];
-  ComputeShaderHash(*shader_a->deferred_compilation_source(), a_sha);
-  ComputeShaderHash(*shader_b->deferred_compilation_source(), b_sha);
+  ComputeShaderHash(
+      *shader_a->deferred_compilation_source(), translator_a, a_sha);
+  ComputeShaderHash(
+      *shader_b->deferred_compilation_source(), translator_b, b_sha);
 
   char sha[kHashLength];
   ComputeProgramHash(a_sha,
diff --git a/gpu/command_buffer/service/memory_program_cache.h b/gpu/command_buffer/service/memory_program_cache.h
index 7d5f808..ed791a8 100644
--- a/gpu/command_buffer/service/memory_program_cache.h
+++ b/gpu/command_buffer/service/memory_program_cache.h
@@ -29,13 +29,17 @@
   virtual ProgramLoadResult LoadLinkedProgram(
       GLuint program,
       Shader* shader_a,
+      const ShaderTranslatorInterface* translator_a,
       Shader* shader_b,
+      const ShaderTranslatorInterface* translator_b,
       const LocationMap* bind_attrib_location_map,
       const ShaderCacheCallback& shader_callback) const OVERRIDE;
   virtual void SaveLinkedProgram(
       GLuint program,
       const Shader* shader_a,
+      const ShaderTranslatorInterface* translator_a,
       const Shader* shader_b,
+      const ShaderTranslatorInterface* translator_b,
       const LocationMap* bind_attrib_location_map,
       const ShaderCacheCallback& shader_callback) OVERRIDE;
 
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc
index b083552..603c21e 100644
--- a/gpu/command_buffer/service/memory_program_cache_unittest.cc
+++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -96,8 +96,8 @@
     ::gfx::GLInterface::SetGLInterface(gl_.get());
 
     vertex_shader_ = shader_manager_.CreateShader(kVertexShaderClientId,
-                                                      kVertexShaderServiceId,
-                                                      GL_VERTEX_SHADER);
+                                                  kVertexShaderServiceId,
+                                                  GL_VERTEX_SHADER);
     fragment_shader_ = shader_manager_.CreateShader(
         kFragmentShaderClientId,
         kFragmentShaderServiceId,
@@ -196,13 +196,16 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
       *vertex_shader_->deferred_compilation_source(),
+      NULL,
       *fragment_shader_->deferred_compilation_source(),
+      NULL,
       NULL));
   EXPECT_EQ(1, shader_cache_count());
 }
@@ -218,13 +221,16 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
       *vertex_shader_->deferred_compilation_source(),
+      NULL,
       *fragment_shader_->deferred_compilation_source(),
+      NULL,
       NULL));
   EXPECT_EQ(1, shader_cache_count());
 
@@ -233,7 +239,9 @@
   cache_->LoadProgram(shader_cache_shader());
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
       *vertex_shader_->deferred_compilation_source(),
+      NULL,
       *fragment_shader_->deferred_compilation_source(),
+      NULL,
       NULL));
 }
 
@@ -248,7 +256,8 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
   EXPECT_EQ(1, shader_cache_count());
@@ -268,8 +277,10 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 
@@ -294,7 +305,8 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
   EXPECT_EQ(1, shader_cache_count());
@@ -317,8 +329,10 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 
@@ -343,7 +357,8 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
@@ -351,8 +366,10 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 }
@@ -368,7 +385,8 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
@@ -379,8 +397,10 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 
@@ -391,8 +411,10 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 }
@@ -412,7 +434,9 @@
   binding_map["test"] = 512;
   cache_->SaveLinkedProgram(kProgramId,
                             vertex_shader_,
+                            NULL,
                             fragment_shader_,
+                            NULL,
                             &binding_map,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
@@ -421,15 +445,19 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
+      NULL,
       &binding_map,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 }
@@ -447,7 +475,8 @@
 
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
@@ -472,18 +501,24 @@
   SetExpectationsForSaveLinkedProgram(kEvictingProgramId, &emulator2);
   cache_->SaveLinkedProgram(kEvictingProgramId,
                             vertex_shader_,
+                            NULL,
                             fragment_shader_,
                             NULL,
+                            NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
       *vertex_shader_->deferred_compilation_source(),
+      NULL,
       *fragment_shader_->deferred_compilation_source(),
+      NULL,
       NULL));
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN, cache_->GetLinkedProgramStatus(
       old_source,
+      NULL,
       *fragment_shader_->deferred_compilation_source(),
+      NULL,
       NULL));
 }
 
@@ -499,13 +534,16 @@
 
   vertex_shader_->UpdateSource("different!");
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
       *vertex_shader_->deferred_compilation_source(),
+      NULL,
       *fragment_shader_->deferred_compilation_source(),
+      NULL,
       NULL));
 }
 
@@ -520,13 +558,16 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
       *vertex_shader_->deferred_compilation_source(),
+      NULL,
       *fragment_shader_->deferred_compilation_source(),
+      NULL,
       NULL));
 
   SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
@@ -535,8 +576,10 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 }
@@ -552,7 +595,8 @@
   ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
 
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
@@ -563,7 +607,8 @@
   }
   ProgramBinaryEmulator emulator2(kBinaryLength, kFormat, test_binary2);
   SetExpectationsForSaveLinkedProgram(kProgramId, &emulator2);
-  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+  cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
+                            fragment_shader_, NULL, NULL,
                             base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                                        base::Unretained(this)));
 
@@ -571,8 +616,10 @@
   EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
       kProgramId,
       vertex_shader_,
+      NULL,
       fragment_shader_,
       NULL,
+      NULL,
       base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
                  base::Unretained(this))));
 }
diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h
index 0964ada..11b8ae6 100644
--- a/gpu/command_buffer/service/mocks.h
+++ b/gpu/command_buffer/service/mocks.h
@@ -91,6 +91,8 @@
   MOCK_CONST_METHOD0(attrib_map, const VariableMap&());
   MOCK_CONST_METHOD0(uniform_map, const VariableMap&());
   MOCK_CONST_METHOD0(name_map, const NameMap&());
+  MOCK_CONST_METHOD0(
+      GetStringForOptionsThatWouldEffectCompilation, std::string());
 };
 
 class MockProgramCache : public ProgramCache {
@@ -98,17 +100,21 @@
   MockProgramCache();
   virtual ~MockProgramCache();
 
-  MOCK_CONST_METHOD5(LoadLinkedProgram, ProgramLoadResult(
+  MOCK_CONST_METHOD7(LoadLinkedProgram, ProgramLoadResult(
       GLuint program,
       Shader* shader_a,
+      const ShaderTranslatorInterface* translator_a,
       Shader* shader_b,
+      const ShaderTranslatorInterface* translator_b,
       const LocationMap* bind_attrib_location_map,
       const ShaderCacheCallback& callback));
 
-  MOCK_METHOD5(SaveLinkedProgram, void(
+  MOCK_METHOD7(SaveLinkedProgram, void(
       GLuint program,
       const Shader* shader_a,
+      const ShaderTranslatorInterface* translator_a,
       const Shader* shader_b,
+      const ShaderTranslatorInterface* translator_b,
       const LocationMap* bind_attrib_location_map,
       const ShaderCacheCallback& callback));
   MOCK_METHOD1(LoadProgram, void(const std::string&));
diff --git a/gpu/command_buffer/service/program_cache.cc b/gpu/command_buffer/service/program_cache.cc
index 48fafab..62e636d 100644
--- a/gpu/command_buffer/service/program_cache.cc
+++ b/gpu/command_buffer/service/program_cache.cc
@@ -4,6 +4,7 @@
 
 #include "gpu/command_buffer/service/program_cache.h"
 
+#include <string>
 #include "base/memory/scoped_ptr.h"
 #include "gpu/command_buffer/service/shader_manager.h"
 
@@ -20,9 +21,10 @@
 }
 
 ProgramCache::CompiledShaderStatus ProgramCache::GetShaderCompilationStatus(
-    const std::string& shader_src) const {
+    const std::string& shader_src,
+    const ShaderTranslatorInterface* translator) const {
   char sha[kHashLength];
-  ComputeShaderHash(shader_src, sha);
+  ComputeShaderHash(shader_src, translator, sha);
   const std::string sha_string(sha, kHashLength);
 
   CompileStatusMap::const_iterator found = shader_status_.find(sha_string);
@@ -35,9 +37,10 @@
 }
 
 void ProgramCache::ShaderCompilationSucceeded(
-    const std::string& shader_src) {
+    const std::string& shader_src,
+    const ShaderTranslatorInterface* translator) {
   char sha[kHashLength];
-  ComputeShaderHash(shader_src, sha);
+  ComputeShaderHash(shader_src, translator, sha);
   const std::string sha_string(sha, kHashLength);
   ShaderCompilationSucceededSha(sha_string);
 }
@@ -54,12 +57,14 @@
 
 ProgramCache::LinkedProgramStatus ProgramCache::GetLinkedProgramStatus(
     const std::string& untranslated_a,
+    const ShaderTranslatorInterface* translator_a,
     const std::string& untranslated_b,
+    const ShaderTranslatorInterface* translator_b,
     const std::map<std::string, GLint>* bind_attrib_location_map) const {
   char a_sha[kHashLength];
   char b_sha[kHashLength];
-  ComputeShaderHash(untranslated_a, a_sha);
-  ComputeShaderHash(untranslated_b, b_sha);
+  ComputeShaderHash(untranslated_a, translator_a, a_sha);
+  ComputeShaderHash(untranslated_b, translator_b, b_sha);
 
   char sha[kHashLength];
   ComputeProgramHash(a_sha,
@@ -78,12 +83,14 @@
 
 void ProgramCache::LinkedProgramCacheSuccess(
     const std::string& shader_a,
+    const ShaderTranslatorInterface* translator_a,
     const std::string& shader_b,
+    const ShaderTranslatorInterface* translator_b,
     const LocationMap* bind_attrib_location_map) {
   char a_sha[kHashLength];
   char b_sha[kHashLength];
-  ComputeShaderHash(shader_a, a_sha);
-  ComputeShaderHash(shader_b, b_sha);
+  ComputeShaderHash(shader_a, translator_a, a_sha);
+  ComputeShaderHash(shader_b, translator_b, b_sha);
   char sha[kHashLength];
   ComputeProgramHash(a_sha,
                      b_sha,
@@ -104,10 +111,15 @@
   shader_status_[shader_b_hash].ref_count++;
 }
 
-void ProgramCache::ComputeShaderHash(const std::string& str,
-                                     char* result) const {
-  base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.c_str()),
-                      str.length(), reinterpret_cast<unsigned char*>(result));
+void ProgramCache::ComputeShaderHash(
+    const std::string& str,
+    const ShaderTranslatorInterface* translator,
+    char* result) const {
+  std::string s((
+      translator ? translator->GetStringForOptionsThatWouldEffectCompilation() :
+                   std::string()) + str);
+  base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(s.c_str()),
+                      s.length(), reinterpret_cast<unsigned char*>(result));
 }
 
 void ProgramCache::Evict(const std::string& program_hash,
diff --git a/gpu/command_buffer/service/program_cache.h b/gpu/command_buffer/service/program_cache.h
index c4b50e8..c42d359 100644
--- a/gpu/command_buffer/service/program_cache.h
+++ b/gpu/command_buffer/service/program_cache.h
@@ -18,6 +18,7 @@
 namespace gles2 {
 
 class Shader;
+class ShaderTranslator;
 
 // Program cache base class for caching linked gpu programs
 class GPU_EXPORT ProgramCache {
@@ -45,13 +46,17 @@
   virtual ~ProgramCache();
 
   CompiledShaderStatus GetShaderCompilationStatus(
-      const std::string& shader_src) const;
-  void ShaderCompilationSucceeded(const std::string& shader_src);
+      const std::string& shader_src,
+      const ShaderTranslatorInterface* translator) const;
+  void ShaderCompilationSucceeded(const std::string& shader_src,
+                                  const ShaderTranslatorInterface* translator);
   void ShaderCompilationSucceededSha(const std::string& sha_string);
 
   LinkedProgramStatus GetLinkedProgramStatus(
-      const std::string& untranslated_a,
-      const std::string& untranslated_b,
+      const std::string& untranslated_shader_a,
+      const ShaderTranslatorInterface* translator_a,
+      const std::string& untranslated_shader_b,
+      const ShaderTranslatorInterface* translator_b,
       const LocationMap* bind_attrib_location_map) const;
 
   // Loads the linked program from the cache.  If the program is not found or
@@ -59,7 +64,9 @@
   virtual ProgramLoadResult LoadLinkedProgram(
       GLuint program,
       Shader* shader_a,
+      const ShaderTranslatorInterface* translator_a,
       Shader* shader_b,
+      const ShaderTranslatorInterface* translator_b,
       const LocationMap* bind_attrib_location_map,
       const ShaderCacheCallback& shader_callback) const = 0;
 
@@ -68,7 +75,9 @@
   virtual void SaveLinkedProgram(
       GLuint program,
       const Shader* shader_a,
+      const ShaderTranslatorInterface* translator_a,
       const Shader* shader_b,
+      const ShaderTranslatorInterface* translator_b,
       const LocationMap* bind_attrib_location_map,
       const ShaderCacheCallback& shader_callback) = 0;
 
@@ -79,7 +88,9 @@
 
   // Only for testing
   void LinkedProgramCacheSuccess(const std::string& shader_a,
+                                 const ShaderTranslatorInterface* translator_a,
                                  const std::string& shader_b,
+                                 const ShaderTranslatorInterface* translator_b,
                                  const LocationMap* bind_attrib_location_map);
 
  protected:
@@ -90,6 +101,7 @@
 
   // result is not null terminated
   void ComputeShaderHash(const std::string& shader,
+                         const ShaderTranslatorInterface* translator,
                          char* result) const;
 
   // result is not null terminated.  hashed shaders are expected to be
diff --git a/gpu/command_buffer/service/program_cache_unittest.cc b/gpu/command_buffer/service/program_cache_unittest.cc
index 46a2fe1..63c032d 100644
--- a/gpu/command_buffer/service/program_cache_unittest.cc
+++ b/gpu/command_buffer/service/program_cache_unittest.cc
@@ -5,8 +5,11 @@
 #include "gpu/command_buffer/service/program_cache.h"
 
 #include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/service/mocks.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::Return;
+
 namespace gpu {
 namespace gles2 {
 
@@ -15,7 +18,9 @@
   virtual ProgramLoadResult LoadLinkedProgram(
       GLuint /* program */,
       Shader* /* shader_a */,
+      const ShaderTranslatorInterface* /* translator_a */,
       Shader* /* shader_b */,
+      const ShaderTranslatorInterface* /* translator_b */,
       const LocationMap* /* bind_attrib_location_map */,
       const ShaderCacheCallback& /* callback */) const OVERRIDE {
     return PROGRAM_LOAD_SUCCESS;
@@ -23,7 +28,9 @@
   virtual void SaveLinkedProgram(
       GLuint /* program */,
       const Shader* /* shader_a */,
+      const ShaderTranslatorInterface* /* translator_b */,
       const Shader* /* shader_b */,
+      const ShaderTranslatorInterface* /* translator_b */,
       const LocationMap* /* bind_attrib_location_map */,
       const ShaderCacheCallback& /* callback */) OVERRIDE { }
 
@@ -32,12 +39,14 @@
   virtual void ClearBackend() OVERRIDE {}
 
   void SaySuccessfullyCached(const std::string& shader1,
+                             const ShaderTranslatorInterface* translator_1,
                              const std::string& shader2,
+                             const ShaderTranslatorInterface* translator_2,
                              std::map<std::string, GLint>* attrib_map) {
     char a_sha[kHashLength];
     char b_sha[kHashLength];
-    ComputeShaderHash(shader1, a_sha);
-    ComputeShaderHash(shader2, b_sha);
+    ComputeShaderHash(shader1, translator_1, a_sha);
+    ComputeShaderHash(shader2, translator_2, b_sha);
 
     char sha[kHashLength];
     ComputeProgramHash(a_sha,
@@ -52,8 +61,9 @@
   }
 
   void ComputeShaderHash(const std::string& shader,
+                         const ShaderTranslatorInterface* translator,
                          char* result) const {
-    ProgramCache::ComputeShaderHash(shader, result);
+    ProgramCache::ComputeShaderHash(shader, translator, result);
   }
 
   void ComputeProgramHash(const char* hashed_shader_0,
@@ -87,22 +97,47 @@
   {
     std::string shader = shader1;
     EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-              cache_->GetShaderCompilationStatus(shader));
-    cache_->ShaderCompilationSucceeded(shader);
+              cache_->GetShaderCompilationStatus(shader, NULL));
+    cache_->ShaderCompilationSucceeded(shader, NULL);
     shader.clear();
   }
   // make sure it was copied
   EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
-            cache_->GetShaderCompilationStatus(shader1));
+            cache_->GetShaderCompilationStatus(shader1, NULL));
+}
+
+TEST_F(ProgramCacheTest, CompilationStatusTranslatorOptionDependent) {
+  MockShaderTranslator translator;
+
+  EXPECT_CALL(translator, GetStringForOptionsThatWouldEffectCompilation())
+      .WillOnce(Return("foo"))   // GetShaderCompilationStatus
+      .WillOnce(Return("foo"))   // ShaderCompilationSucceeded
+      .WillOnce(Return("foo"))   // GetShaderCompilationStatus
+      .WillOnce(Return("bar"));  // GetShaderCompilationStatus
+
+  const std::string shader1 = "abcd1234";
+  {
+    std::string shader = shader1;
+    EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+              cache_->GetShaderCompilationStatus(shader, &translator));
+    cache_->ShaderCompilationSucceeded(shader, &translator);
+    shader.clear();
+  }
+  // make sure it was copied
+  EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
+            cache_->GetShaderCompilationStatus(shader1, &translator));
+  // make sure if the options change it's not copied.
+  EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+            cache_->GetShaderCompilationStatus(shader1, &translator));
 }
 
 TEST_F(ProgramCacheTest, CompilationUnknownOnSourceChange) {
   std::string shader1 = "abcd1234";
-  cache_->ShaderCompilationSucceeded(shader1);
+  cache_->ShaderCompilationSucceeded(shader1, NULL);
 
   shader1 = "different!";
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader1));
+            cache_->GetShaderCompilationStatus(shader1, NULL));
 }
 
 TEST_F(ProgramCacheTest, LinkStatusSave) {
@@ -112,47 +147,49 @@
     std::string shader_a = shader1;
     std::string shader_b = shader2;
     EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-              cache_->GetLinkedProgramStatus(shader_a, shader_b, NULL));
-    cache_->SaySuccessfullyCached(shader_a, shader_b, NULL);
+              cache_->GetLinkedProgramStatus(
+                  shader_a, NULL, shader_b, NULL, NULL));
+    cache_->SaySuccessfullyCached(shader_a, NULL, shader_b, NULL, NULL);
 
     shader_a.clear();
     shader_b.clear();
   }
   // make sure it was copied
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED,
-            cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+            cache_->GetLinkedProgramStatus(
+                shader1, NULL, shader2, NULL, NULL));
 }
 
 TEST_F(ProgramCacheTest, LinkUnknownOnFragmentSourceChange) {
   const std::string shader1 = "abcd1234";
   std::string shader2 = "abcda sda b1~#4 bbbbb1234";
-  cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+  cache_->SaySuccessfullyCached(shader1, NULL, shader2, NULL, NULL);
 
   shader2 = "different!";
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader2, NULL, NULL));
 }
 
 TEST_F(ProgramCacheTest, LinkUnknownOnVertexSourceChange) {
   std::string shader1 = "abcd1234";
   const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
-  cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+  cache_->SaySuccessfullyCached(shader1, NULL, shader2, NULL, NULL);
 
   shader1 = "different!";
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader2, NULL, NULL));
 }
 
 TEST_F(ProgramCacheTest, StatusEviction) {
   const std::string shader1 = "abcd1234";
   const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
-  cache_->ShaderCompilationSucceeded(shader1);
-  cache_->ShaderCompilationSucceeded(shader2);
-  cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+  cache_->ShaderCompilationSucceeded(shader1, NULL);
+  cache_->ShaderCompilationSucceeded(shader2, NULL);
+  cache_->SaySuccessfullyCached(shader1, NULL, shader2, NULL, NULL);
   char a_sha[ProgramCache::kHashLength];
   char b_sha[ProgramCache::kHashLength];
-  cache_->ComputeShaderHash(shader1, a_sha);
-  cache_->ComputeShaderHash(shader2, b_sha);
+  cache_->ComputeShaderHash(shader1, NULL, a_sha);
+  cache_->ComputeShaderHash(shader2, NULL, b_sha);
 
   char sha[ProgramCache::kHashLength];
   cache_->ComputeProgramHash(a_sha,
@@ -163,30 +200,30 @@
                 std::string(a_sha, ProgramCache::kHashLength),
                 std::string(b_sha, ProgramCache::kHashLength));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader1));
+            cache_->GetShaderCompilationStatus(shader1, NULL));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader2));
+            cache_->GetShaderCompilationStatus(shader2, NULL));
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader2, NULL, NULL));
 }
 
 TEST_F(ProgramCacheTest, EvictionWithReusedShader) {
   const std::string shader1 = "abcd1234";
   const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
   const std::string shader3 = "asbjbbjj239a";
-  cache_->ShaderCompilationSucceeded(shader1);
-  cache_->ShaderCompilationSucceeded(shader2);
-  cache_->SaySuccessfullyCached(shader1, shader2, NULL);
-  cache_->ShaderCompilationSucceeded(shader1);
-  cache_->ShaderCompilationSucceeded(shader3);
-  cache_->SaySuccessfullyCached(shader1, shader3, NULL);
+  cache_->ShaderCompilationSucceeded(shader1, NULL);
+  cache_->ShaderCompilationSucceeded(shader2, NULL);
+  cache_->SaySuccessfullyCached(shader1, NULL, shader2, NULL, NULL);
+  cache_->ShaderCompilationSucceeded(shader1, NULL);
+  cache_->ShaderCompilationSucceeded(shader3, NULL);
+  cache_->SaySuccessfullyCached(shader1, NULL, shader3, NULL, NULL);
 
   char a_sha[ProgramCache::kHashLength];
   char b_sha[ProgramCache::kHashLength];
   char c_sha[ProgramCache::kHashLength];
-  cache_->ComputeShaderHash(shader1, a_sha);
-  cache_->ComputeShaderHash(shader2, b_sha);
-  cache_->ComputeShaderHash(shader3, c_sha);
+  cache_->ComputeShaderHash(shader1, NULL, a_sha);
+  cache_->ComputeShaderHash(shader2, NULL, b_sha);
+  cache_->ComputeShaderHash(shader3, NULL, c_sha);
 
   char sha[ProgramCache::kHashLength];
   cache_->ComputeProgramHash(a_sha,
@@ -197,15 +234,15 @@
                 std::string(a_sha, ProgramCache::kHashLength),
                 std::string(b_sha, ProgramCache::kHashLength));
   EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
-            cache_->GetShaderCompilationStatus(shader1));
+            cache_->GetShaderCompilationStatus(shader1, NULL));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader2));
+            cache_->GetShaderCompilationStatus(shader2, NULL));
   EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
-            cache_->GetShaderCompilationStatus(shader3));
+            cache_->GetShaderCompilationStatus(shader3, NULL));
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader2, NULL, NULL));
   EXPECT_EQ(ProgramCache::LINK_SUCCEEDED,
-            cache_->GetLinkedProgramStatus(shader1, shader3, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader3, NULL, NULL));
 
 
   cache_->ComputeProgramHash(a_sha,
@@ -216,37 +253,37 @@
                 std::string(a_sha, ProgramCache::kHashLength),
                 std::string(c_sha, ProgramCache::kHashLength));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader1));
+            cache_->GetShaderCompilationStatus(shader1, NULL));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader2));
+            cache_->GetShaderCompilationStatus(shader2, NULL));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader3));
+            cache_->GetShaderCompilationStatus(shader3, NULL));
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader2, NULL, NULL));
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader3, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader3, NULL, NULL));
 }
 
 TEST_F(ProgramCacheTest, StatusClear) {
   const std::string shader1 = "abcd1234";
   const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
   const std::string shader3 = "asbjbbjj239a";
-  cache_->ShaderCompilationSucceeded(shader1);
-  cache_->ShaderCompilationSucceeded(shader2);
-  cache_->SaySuccessfullyCached(shader1, shader2, NULL);
-  cache_->ShaderCompilationSucceeded(shader3);
-  cache_->SaySuccessfullyCached(shader1, shader3, NULL);
+  cache_->ShaderCompilationSucceeded(shader1, NULL);
+  cache_->ShaderCompilationSucceeded(shader2, NULL);
+  cache_->SaySuccessfullyCached(shader1, NULL, shader2, NULL, NULL);
+  cache_->ShaderCompilationSucceeded(shader3, NULL);
+  cache_->SaySuccessfullyCached(shader1, NULL, shader3, NULL, NULL);
   cache_->Clear();
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader1));
+            cache_->GetShaderCompilationStatus(shader1, NULL));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader2));
+            cache_->GetShaderCompilationStatus(shader2, NULL));
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(shader3));
+            cache_->GetShaderCompilationStatus(shader3, NULL));
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader2, NULL, NULL));
   EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
-            cache_->GetLinkedProgramStatus(shader1, shader3, NULL));
+            cache_->GetLinkedProgramStatus(shader1, NULL, shader3, NULL, NULL));
 }
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index 0fecd0a..d362cde 100644
--- a/gpu/command_buffer/service/program_manager.cc
+++ b/gpu/command_buffer/service/program_manager.cc
@@ -446,8 +446,8 @@
   TimeTicks before = TimeTicks::HighResNow();
   if (program_cache_ &&
       program_cache_->GetShaderCompilationStatus(
-          shader->source() ? *shader->source() : std::string()) ==
-          ProgramCache::COMPILATION_SUCCEEDED) {
+          shader->source() ? *shader->source() : std::string(),
+          translator) == ProgramCache::COMPILATION_SUCCEEDED) {
     shader->SetStatus(true, "", translator);
     shader->FlagSourceAsCompiled(false);
     UMA_HISTOGRAM_CUSTOM_COUNTS(
@@ -508,7 +508,8 @@
     shader->SetStatus(true, "", translator);
     if (program_cache_) {
       const char* untranslated_source = source ? source->c_str() : "";
-      program_cache_->ShaderCompilationSucceeded(untranslated_source);
+      program_cache_->ShaderCompilationSucceeded(
+          untranslated_source, translator);
     }
   } else {
     // We cannot reach here if we are using the shader translator.
@@ -552,14 +553,18 @@
   if (cache) {
     ProgramCache::LinkedProgramStatus status = cache->GetLinkedProgramStatus(
         *attached_shaders_[0]->deferred_compilation_source(),
+        vertex_translator,
         *attached_shaders_[1]->deferred_compilation_source(),
+        fragment_translator,
         &bind_attrib_location_map_);
 
     if (status == ProgramCache::LINK_SUCCEEDED) {
       ProgramCache::ProgramLoadResult success = cache->LoadLinkedProgram(
                   service_id(),
                   attached_shaders_[0],
+                  vertex_translator,
                   attached_shaders_[1],
+                  fragment_translator,
                   &bind_attrib_location_map_,
                   shader_callback);
       link = success != ProgramCache::PROGRAM_LOAD_SUCCESS;
@@ -581,7 +586,7 @@
                                        attached_shaders_[i],
                                        translator,
                                        feature_info);
-          CHECK(shader->IsValid());
+          DCHECK(shader->IsValid());
         }
       }
     }
@@ -606,7 +611,9 @@
       if (cache) {
         cache->SaveLinkedProgram(service_id(),
                                  attached_shaders_[0],
+                                 vertex_translator,
                                  attached_shaders_[1],
+                                 fragment_translator,
                                  &bind_attrib_location_map_,
                                  shader_callback);
       }
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index 8770e93..fce5ac3 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -1192,8 +1192,8 @@
   }
 
   void SetShadersCompiled() {
-    cache_->ShaderCompilationSucceeded(*vertex_shader_->source());
-    cache_->ShaderCompilationSucceeded(*fragment_shader_->source());
+    cache_->ShaderCompilationSucceeded(*vertex_shader_->source(), NULL);
+    cache_->ShaderCompilationSucceeded(*fragment_shader_->source(), NULL);
     vertex_shader_->SetStatus(true, NULL, NULL);
     fragment_shader_->SetStatus(true, NULL, NULL);
     vertex_shader_->FlagSourceAsCompiled(true);
@@ -1209,7 +1209,9 @@
   void SetProgramCached() {
     cache_->LinkedProgramCacheSuccess(
         vertex_shader_->source()->c_str(),
+        NULL,
         fragment_shader_->source()->c_str(),
+        NULL,
         &program_->bind_attrib_location_map());
   }
 
@@ -1226,7 +1228,9 @@
     EXPECT_CALL(*cache_.get(), SaveLinkedProgram(
         program->service_id(),
         vertex_shader,
+        NULL,
         fragment_shader,
+        NULL,
         &program->bind_attrib_location_map(),
         _)).Times(1);
   }
@@ -1244,7 +1248,9 @@
     EXPECT_CALL(*cache_.get(), SaveLinkedProgram(
         program->service_id(),
         vertex_shader,
+        NULL,
         fragment_shader,
+        NULL,
         &program->bind_attrib_location_map(),
         _)).Times(0);
   }
@@ -1266,7 +1272,9 @@
     EXPECT_CALL(*cache_.get(),
                 LoadLinkedProgram(service_program_id,
                                   vertex_shader,
+                                  NULL,
                                   fragment_shader,
+                                  NULL,
                                   &program->bind_attrib_location_map(),
                                   _))
         .WillOnce(Return(result));
@@ -1360,7 +1368,8 @@
   scoped_refptr<FeatureInfo> info(new FeatureInfo());
   manager_.DoCompileShader(vertex_shader_, NULL, info.get());
   EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
-            cache_->GetShaderCompilationStatus(*vertex_shader_->source()));
+            cache_->GetShaderCompilationStatus(
+                *vertex_shader_->source(), NULL));
 }
 
 TEST_F(ProgramManagerWithCacheTest, CacheUnknownAfterShaderError) {
@@ -1368,11 +1377,12 @@
   scoped_refptr<FeatureInfo> info(new FeatureInfo());
   manager_.DoCompileShader(vertex_shader_, NULL, info.get());
   EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
-            cache_->GetShaderCompilationStatus(*vertex_shader_->source()));
+            cache_->GetShaderCompilationStatus(
+                *vertex_shader_->source(), NULL));
 }
 
 TEST_F(ProgramManagerWithCacheTest, NoCompileWhenShaderCached) {
-  cache_->ShaderCompilationSucceeded(vertex_shader_->source()->c_str());
+  cache_->ShaderCompilationSucceeded(vertex_shader_->source()->c_str(), NULL);
   SetExpectationsForNoCompile(vertex_shader_);
   scoped_refptr<FeatureInfo> info(new FeatureInfo());
   manager_.DoCompileShader(vertex_shader_, NULL, info.get());
diff --git a/gpu/command_buffer/service/safe_shared_memory_pool.cc b/gpu/command_buffer/service/safe_shared_memory_pool.cc
index 05fce77..9496967 100644
--- a/gpu/command_buffer/service/safe_shared_memory_pool.cc
+++ b/gpu/command_buffer/service/safe_shared_memory_pool.cc
@@ -120,7 +120,7 @@
   if (!shared_memory->ShareToProcess(
       base::GetCurrentProcessHandle(),
       &duped_shared_memory_handle)) {
-    LOG(ERROR) << "Failed SharedMemory::ShareToProcess";
+    PLOG(ERROR) << "Failed SharedMemory::ShareToProcess";
     LOG(ERROR) << "Total handles acquired " << handles_acquired_;
     LOG(ERROR) << "Total handles open " << handles_consumed_;
     LOG(ERROR) << "Total address space " << address_space_consumed_;
@@ -134,7 +134,7 @@
       new SharedMemory(duped_shared_memory_handle, false));
   // Map the shared memory into this process. This validates the size.
   if (!duped_shared_memory->Map(size)) {
-    LOG(ERROR) << "Failed SharedMemory::Map";
+    PLOG(ERROR) << "Failed SharedMemory::Map(" << size << ")";
     LOG(ERROR) << "Total handles acquired " << handles_acquired_;
     LOG(ERROR) << "Total handles open " << handles_consumed_;
     LOG(ERROR) << "Total address space " << address_space_consumed_;
diff --git a/gpu/command_buffer/service/shader_translator.cc b/gpu/command_buffer/service/shader_translator.cc
index 2e5541d..3bd2a97 100644
--- a/gpu/command_buffer/service/shader_translator.cc
+++ b/gpu/command_buffer/service/shader_translator.cc
@@ -8,19 +8,24 @@
 #include <algorithm>
 
 #include "base/at_exit.h"
+#include "base/debug/trace_event.h"
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
 
 namespace {
 
 using gpu::gles2::ShaderTranslator;
 
 void FinalizeShaderTranslator(void* /* dummy */) {
+  TRACE_EVENT0("gpu", "ShFinalize");
   ShFinalize();
 }
 
 bool InitializeShaderTranslator() {
   static bool initialized = false;
-  if (!initialized && ShInitialize()) {
+  if (!initialized) {
+    TRACE_EVENT0("gpu", "ShInitialize");
+    CHECK(ShInitialize());
     base::AtExitManager::RegisterCallback(&FinalizeShaderTranslator, NULL);
     initialized = true;
   }
@@ -137,14 +142,32 @@
   ShShaderOutput shader_output =
       (glsl_implementation_type == kGlslES ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT);
 
-  compiler_ = ShConstructCompiler(
-      shader_type, shader_spec, shader_output, resources);
+  {
+    TRACE_EVENT0("gpu", "ShConstructCompiler");
+    compiler_ = ShConstructCompiler(
+        shader_type, shader_spec, shader_output, resources);
+  }
+  compiler_options_ = *resources;
   implementation_is_glsl_es_ = (glsl_implementation_type == kGlslES);
   needs_built_in_function_emulation_ =
       (glsl_built_in_function_behavior == kGlslBuiltInFunctionEmulated);
   return compiler_ != NULL;
 }
 
+int ShaderTranslator::GetCompileOptions() const {
+  int compile_options =
+      SH_OBJECT_CODE | SH_ATTRIBUTES_UNIFORMS |
+      SH_MAP_LONG_VARIABLE_NAMES | SH_ENFORCE_PACKING_RESTRICTIONS |
+      SH_LIMIT_EXPRESSION_COMPLEXITY | SH_LIMIT_CALL_STACK_DEPTH;
+
+  compile_options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
+
+  if (needs_built_in_function_emulation_)
+    compile_options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
+
+  return compile_options;
+}
+
 bool ShaderTranslator::Translate(const char* shader) {
   // Make sure this instance is initialized.
   DCHECK(compiler_ != NULL);
@@ -152,16 +175,11 @@
   ClearResults();
 
   bool success = false;
-  int compile_options =
-      SH_OBJECT_CODE | SH_ATTRIBUTES_UNIFORMS |
-      SH_MAP_LONG_VARIABLE_NAMES | SH_ENFORCE_PACKING_RESTRICTIONS;
-
-  compile_options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
-
-  if (needs_built_in_function_emulation_)
-    compile_options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
-  if (ShCompile(compiler_, &shader, 1, compile_options)) {
-    success = true;
+  {
+    TRACE_EVENT0("gpu", "ShCompile");
+    success = !!ShCompile(compiler_, &shader, 1, GetCompileOptions());
+  }
+  if (success) {
     // Get translated shader.
     ANGLEGetInfoType obj_code_len = 0;
     ShGetInfo(compiler_, SH_OBJECT_CODE_LENGTH, &obj_code_len);
@@ -189,6 +207,60 @@
   return success;
 }
 
+std::string ShaderTranslator::GetStringForOptionsThatWouldEffectCompilation()
+    const {
+  const size_t kNumIntFields = 15;
+  const size_t kNumEnumFields = 1;
+  const size_t kNumFunctionPointerFields = 1;
+  struct MustMatchShBuiltInResource {
+    typedef khronos_uint64_t (*FunctionPointer)(const char*, size_t);
+    enum Enum {
+      kFirst,
+    };
+    int int_fields[kNumIntFields];
+    FunctionPointer pointer_fields[kNumFunctionPointerFields];
+    Enum enum_fields[kNumEnumFields];
+  };
+  // If this assert fails most likely that means something below needs updating.
+  COMPILE_ASSERT(
+      sizeof(ShBuiltInResources) == sizeof(MustMatchShBuiltInResource),
+      Fields_Have_Changed_In_ShBuiltInResource_So_Update_Below);
+
+  return std::string(
+      ":CompileOptions:" +
+      base::IntToString(GetCompileOptions()) +
+      ":MaxVertexAttribs:" +
+      base::IntToString(compiler_options_.MaxVertexAttribs) +
+      ":MaxVertexUniformVectors:" +
+      base::IntToString(compiler_options_.MaxVertexUniformVectors) +
+      ":MaxVaryingVectors:" +
+      base::IntToString(compiler_options_.MaxVaryingVectors) +
+      ":MaxVertexTextureImageUnits:" +
+      base::IntToString(compiler_options_.MaxVertexTextureImageUnits) +
+      ":MaxCombinedTextureImageUnits:" +
+      base::IntToString(compiler_options_.MaxCombinedTextureImageUnits) +
+      ":MaxTextureImageUnits:" +
+      base::IntToString(compiler_options_.MaxTextureImageUnits) +
+      ":MaxFragmentUniformVectors:" +
+      base::IntToString(compiler_options_.MaxFragmentUniformVectors) +
+      ":MaxDrawBuffers:" +
+      base::IntToString(compiler_options_.MaxDrawBuffers) +
+      ":OES_standard_derivatives:" +
+      base::IntToString(compiler_options_.OES_standard_derivatives) +
+      ":OES_EGL_image_external:" +
+      base::IntToString(compiler_options_.OES_EGL_image_external) +
+      ":ARB_texture_rectangle:" +
+      base::IntToString(compiler_options_.ARB_texture_rectangle) +
+      ":EXT_draw_buffers:" +
+      base::IntToString(compiler_options_.EXT_draw_buffers) +
+      ":FragmentPrecisionHigh:" +
+      base::IntToString(compiler_options_.FragmentPrecisionHigh) +
+      ":MaxExpressionComplexity:" +
+      base::IntToString(compiler_options_.MaxExpressionComplexity) +
+      ":MaxCallStackDepth:" +
+      base::IntToString(compiler_options_.MaxCallStackDepth));
+}
+
 const char* ShaderTranslator::translated_shader() const {
   return translated_shader_.get();
 }
diff --git a/gpu/command_buffer/service/shader_translator.h b/gpu/command_buffer/service/shader_translator.h
index 9260171..5904f73 100644
--- a/gpu/command_buffer/service/shader_translator.h
+++ b/gpu/command_buffer/service/shader_translator.h
@@ -84,6 +84,10 @@
   virtual const VariableMap& uniform_map() const = 0;
   virtual const NameMap& name_map() const = 0;
 
+  // Return a string that is unique for a specfic set of options that would
+  // possibly effect compilation.
+  virtual std::string GetStringForOptionsThatWouldEffectCompilation() const = 0;
+
  protected:
   virtual ~ShaderTranslatorInterface() {}
 };
@@ -126,6 +130,9 @@
   virtual const VariableMap& uniform_map() const OVERRIDE;
   virtual const NameMap& name_map() const OVERRIDE;
 
+  virtual std::string GetStringForOptionsThatWouldEffectCompilation() const
+      OVERRIDE;
+
   void AddDestructionObserver(DestructionObserver* observer);
   void RemoveDestructionObserver(DestructionObserver* observer);
 
@@ -134,8 +141,10 @@
 
   virtual ~ShaderTranslator();
   void ClearResults();
+  int GetCompileOptions() const;
 
   ShHandle compiler_;
+  ShBuiltInResources compiler_options_;
   scoped_ptr<char[]> translated_shader_;
   scoped_ptr<char[]> info_log_;
   VariableMap attrib_map_;
diff --git a/gpu/command_buffer/service/shader_translator_unittest.cc b/gpu/command_buffer/service/shader_translator_unittest.cc
index 469028d..a08851a 100644
--- a/gpu/command_buffer/service/shader_translator_unittest.cc
+++ b/gpu/command_buffer/service/shader_translator_unittest.cc
@@ -19,6 +19,8 @@
  protected:
   virtual void SetUp() {
     ShBuiltInResources resources;
+    resources.MaxExpressionComplexity = 32;
+    resources.MaxCallStackDepth = 32;
     ShInitBuiltInResources(&resources);
     vertex_translator_ = new ShaderTranslator();
     fragment_translator_ = new ShaderTranslator();
@@ -63,7 +65,7 @@
   // Info log must be NULL.
   EXPECT_TRUE(vertex_translator_->info_log() == NULL);
   // Translated shader must be valid and non-empty.
-  EXPECT_TRUE(vertex_translator_->translated_shader() != NULL);
+  ASSERT_TRUE(vertex_translator_->translated_shader() != NULL);
   EXPECT_GT(strlen(vertex_translator_->translated_shader()), 0u);
   // There should be no attributes or uniforms.
   EXPECT_TRUE(vertex_translator_->attrib_map().empty());
@@ -80,7 +82,7 @@
   // An invalid shader should fail.
   EXPECT_FALSE(vertex_translator_->Translate(bad_shader));
   // Info log must be valid and non-empty.
-  EXPECT_TRUE(vertex_translator_->info_log() != NULL);
+  ASSERT_TRUE(vertex_translator_->info_log() != NULL);
   EXPECT_GT(strlen(vertex_translator_->info_log()), 0u);
   // Translated shader must be NULL.
   EXPECT_TRUE(vertex_translator_->translated_shader() == NULL);
@@ -91,7 +93,7 @@
   // Try a good shader after bad.
   EXPECT_TRUE(vertex_translator_->Translate(good_shader));
   EXPECT_TRUE(vertex_translator_->info_log() == NULL);
-  EXPECT_TRUE(vertex_translator_->translated_shader() != NULL);
+  ASSERT_TRUE(vertex_translator_->translated_shader() != NULL);
   EXPECT_GT(strlen(vertex_translator_->translated_shader()), 0u);
 }
 
@@ -106,7 +108,7 @@
   // Info log must be NULL.
   EXPECT_TRUE(fragment_translator_->info_log() == NULL);
   // Translated shader must be valid and non-empty.
-  EXPECT_TRUE(fragment_translator_->translated_shader() != NULL);
+  ASSERT_TRUE(fragment_translator_->translated_shader() != NULL);
   EXPECT_GT(strlen(fragment_translator_->translated_shader()), 0u);
   // There should be no attributes or uniforms.
   EXPECT_TRUE(fragment_translator_->attrib_map().empty());
@@ -119,7 +121,7 @@
   // An invalid shader should fail.
   EXPECT_FALSE(fragment_translator_->Translate(shader));
   // Info log must be valid and non-empty.
-  EXPECT_TRUE(fragment_translator_->info_log() != NULL);
+  ASSERT_TRUE(fragment_translator_->info_log() != NULL);
   EXPECT_GT(strlen(fragment_translator_->info_log()), 0u);
   // Translated shader must be NULL.
   EXPECT_TRUE(fragment_translator_->translated_shader() == NULL);
@@ -139,7 +141,7 @@
   // Info log must be NULL.
   EXPECT_TRUE(vertex_translator_->info_log() == NULL);
   // Translated shader must be valid and non-empty.
-  EXPECT_TRUE(vertex_translator_->translated_shader() != NULL);
+  ASSERT_TRUE(vertex_translator_->translated_shader() != NULL);
   EXPECT_GT(strlen(vertex_translator_->translated_shader()), 0u);
   // There should be no uniforms.
   EXPECT_TRUE(vertex_translator_->uniform_map().empty());
@@ -174,7 +176,7 @@
   // Info log must be NULL.
   EXPECT_TRUE(fragment_translator_->info_log() == NULL);
   // Translated shader must be valid and non-empty.
-  EXPECT_TRUE(fragment_translator_->translated_shader() != NULL);
+  ASSERT_TRUE(fragment_translator_->translated_shader() != NULL);
   EXPECT_GT(strlen(fragment_translator_->translated_shader()), 0u);
   // There should be no attributes.
   EXPECT_TRUE(fragment_translator_->attrib_map().empty());
@@ -213,12 +215,49 @@
   // Info log must be NULL.
   EXPECT_TRUE(vertex_translator_->info_log() == NULL);
   // Translated shader must be valid and non-empty.
-  EXPECT_TRUE(vertex_translator_->translated_shader() != NULL);
+  ASSERT_TRUE(vertex_translator_->translated_shader() != NULL);
   EXPECT_TRUE(strstr(vertex_translator_->translated_shader(),
                      "webgl_dot_emu") != NULL);
 }
 #endif
 
+TEST_F(ShaderTranslatorTest, OptionsString) {
+  scoped_refptr<ShaderTranslator> translator_1 = new ShaderTranslator();
+  scoped_refptr<ShaderTranslator> translator_2 = new ShaderTranslator();
+  scoped_refptr<ShaderTranslator> translator_3 = new ShaderTranslator();
+
+  ShBuiltInResources resources;
+  ShInitBuiltInResources(&resources);
+
+  ASSERT_TRUE(translator_1->Init(
+      SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources,
+      ShaderTranslatorInterface::kGlsl,
+      ShaderTranslatorInterface::kGlslBuiltInFunctionEmulated));
+  ASSERT_TRUE(translator_2->Init(
+      SH_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources,
+      ShaderTranslatorInterface::kGlsl,
+      ShaderTranslatorInterface::kGlslBuiltInFunctionOriginal));
+  resources.EXT_draw_buffers = 1;
+  ASSERT_TRUE(translator_3->Init(
+      SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources,
+      ShaderTranslatorInterface::kGlsl,
+      ShaderTranslatorInterface::kGlslBuiltInFunctionEmulated));
+
+  std::string options_1(
+      translator_1->GetStringForOptionsThatWouldEffectCompilation());
+  std::string options_2(
+      translator_1->GetStringForOptionsThatWouldEffectCompilation());
+  std::string options_3(
+      translator_2->GetStringForOptionsThatWouldEffectCompilation());
+  std::string options_4(
+      translator_3->GetStringForOptionsThatWouldEffectCompilation());
+
+  EXPECT_EQ(options_1, options_2);
+  EXPECT_NE(options_1, options_3);
+  EXPECT_NE(options_1, options_4);
+  EXPECT_NE(options_3, options_4);
+}
+
 }  // namespace gles2
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/service/test_helper.cc b/gpu/command_buffer/service/test_helper.cc
index 5b3f57c..074e301 100644
--- a/gpu/command_buffer/service/test_helper.cc
+++ b/gpu/command_buffer/service/test_helper.cc
@@ -285,12 +285,6 @@
   EXPECT_CALL(*gl, GetString(GL_EXTENSIONS))
       .WillOnce(Return(reinterpret_cast<const uint8*>(extensions)))
       .RetiresOnSaturation();
-  EXPECT_CALL(*gl, GetString(GL_VENDOR))
-      .WillOnce(Return(reinterpret_cast<const uint8*>("")))
-      .RetiresOnSaturation();
-  EXPECT_CALL(*gl, GetString(GL_RENDERER))
-      .WillOnce(Return(reinterpret_cast<const uint8*>("")))
-      .RetiresOnSaturation();
   EXPECT_CALL(*gl, GetString(GL_VERSION))
       .WillOnce(Return(reinterpret_cast<const uint8*>(version)))
       .RetiresOnSaturation();
@@ -523,11 +517,12 @@
 
 void TestHelper::SetTexParameterWithExpectations(
     ::gfx::MockGLInterface* gl, MockErrorState* error_state,
-    TextureManager* manager, Texture* texture,
+    TextureManager* manager, TextureRef* texture_ref,
     GLenum pname, GLint value, GLenum error) {
   if (error == GL_NO_ERROR) {
     if (pname != GL_TEXTURE_POOL_CHROMIUM) {
-      EXPECT_CALL(*gl, TexParameteri(texture->target(), pname, value))
+      EXPECT_CALL(*gl, TexParameteri(texture_ref->texture()->target(),
+                                     pname, value))
           .Times(1)
           .RetiresOnSaturation();
     }
@@ -540,7 +535,7 @@
         .Times(1)
         .RetiresOnSaturation();
   }
-  manager->SetParameter("", error_state, texture, pname, value);
+  manager->SetParameter("", error_state, texture_ref, pname, value);
 }
 
 ScopedGLImplementationSetter::ScopedGLImplementationSetter(
diff --git a/gpu/command_buffer/service/test_helper.h b/gpu/command_buffer/service/test_helper.h
index 41cb3aa..11f7004 100644
--- a/gpu/command_buffer/service/test_helper.h
+++ b/gpu/command_buffer/service/test_helper.h
@@ -15,7 +15,7 @@
 class Buffer;
 class BufferManager;
 class MockErrorState;
-class Texture;
+class TextureRef;
 class TextureManager;
 
 class TestHelper {
@@ -98,7 +98,7 @@
 
   static void SetTexParameterWithExpectations(
       ::gfx::MockGLInterface* gl, MockErrorState* error_state,
-      TextureManager* manager, Texture* texture,
+      TextureManager* manager, TextureRef* texture_ref,
       GLenum pname, GLint value, GLenum error);
 
  private:
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index d64ac25..e2fac59 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -8,6 +8,7 @@
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/service/error_state.h"
 #include "gpu/command_buffer/service/feature_info.h"
+#include "gpu/command_buffer/service/framebuffer_manager.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
@@ -87,10 +88,9 @@
   DCHECK_EQ(0u, memory_tracker_unmanaged_->GetMemRepresented());
 }
 
-Texture::Texture(TextureManager* manager, GLuint service_id)
-    : manager_(manager),
+Texture::Texture(GLuint service_id)
+    : memory_tracking_ref_(NULL),
       service_id_(service_id),
-      deleted_(false),
       cleared_(true),
       num_uncleared_mips_(0),
       target_(0),
@@ -109,24 +109,48 @@
       owned_(true),
       stream_texture_(false),
       immutable_(false),
-      estimated_size_(0) {
-  if (manager_) {
-    manager_->StartTracking(this);
-  }
+      estimated_size_(0),
+      can_render_condition_(CAN_RENDER_ALWAYS) {
 }
 
 Texture::~Texture() {
-  if (manager_) {
-    if (owned_ && manager_->have_context_) {
+}
+
+void Texture::AddTextureRef(TextureRef* ref) {
+  DCHECK(refs_.find(ref) == refs_.end());
+  refs_.insert(ref);
+  if (!memory_tracking_ref_) {
+    memory_tracking_ref_ = ref;
+    GetMemTracker()->TrackMemAlloc(estimated_size());
+  }
+}
+
+void Texture::RemoveTextureRef(TextureRef* ref, bool have_context) {
+  if (memory_tracking_ref_ == ref) {
+    GetMemTracker()->TrackMemFree(estimated_size());
+    memory_tracking_ref_ = NULL;
+  }
+  size_t result = refs_.erase(ref);
+  DCHECK_EQ(result, 1u);
+  if (refs_.empty()) {
+    if (owned_ && have_context) {
       GLuint id = service_id();
       glDeleteTextures(1, &id);
     }
-    MarkAsDeleted();
-    manager_->StopTracking(this);
-    manager_ = NULL;
+    delete this;
+  } else if (memory_tracking_ref_ == NULL) {
+    // TODO(piman): tune ownership semantics for cross-context group shared
+    // textures.
+    memory_tracking_ref_ = *refs_.begin();
+    GetMemTracker()->TrackMemAlloc(estimated_size());
   }
 }
 
+MemoryTypeTracker* Texture::GetMemTracker() {
+  DCHECK(memory_tracking_ref_);
+  return memory_tracking_ref_->manager()->GetMemTracker(pool_);
+}
+
 Texture::LevelInfo::LevelInfo()
     : cleared(true),
       target(0),
@@ -159,26 +183,59 @@
 Texture::LevelInfo::~LevelInfo() {
 }
 
-bool Texture::CanRender(const FeatureInfo* feature_info) const {
-  if (target_ == 0) {
-    return false;
-  }
-  bool needs_mips = NeedsMips();
-  if ((npot() && !feature_info->feature_flags().npot_ok) ||
-      (target_ == GL_TEXTURE_RECTANGLE_ARB)) {
-    return !needs_mips &&
-           wrap_s_ == GL_CLAMP_TO_EDGE &&
-           wrap_t_ == GL_CLAMP_TO_EDGE;
-  }
-  if (needs_mips) {
-    if (target_ == GL_TEXTURE_2D) {
-      return texture_complete();
-    } else {
-      return texture_complete() && cube_complete();
+Texture::CanRenderCondition Texture::GetCanRenderCondition() const {
+  if (target_ == 0)
+    return CAN_RENDER_ALWAYS;
+
+  if (target_ == GL_TEXTURE_EXTERNAL_OES) {
+    if (!IsStreamTexture()) {
+      return CAN_RENDER_NEVER;
     }
   } else {
-    return true;
+    if (level_infos_.empty()) {
+      return CAN_RENDER_NEVER;
+    }
+
+    const Texture::LevelInfo& first_face = level_infos_[0][0];
+    if (first_face.width == 0 ||
+        first_face.height == 0 ||
+        first_face.depth == 0) {
+      return CAN_RENDER_NEVER;
+    }
   }
+
+  bool needs_mips = NeedsMips();
+  if (needs_mips) {
+    if (!texture_complete())
+      return CAN_RENDER_NEVER;
+    if (target_ == GL_TEXTURE_CUBE_MAP && !cube_complete())
+      return CAN_RENDER_NEVER;
+  }
+
+  bool is_npot_compatible = !needs_mips &&
+      wrap_s_ == GL_CLAMP_TO_EDGE &&
+      wrap_t_ == GL_CLAMP_TO_EDGE;
+
+  if (!is_npot_compatible) {
+    if (target_ == GL_TEXTURE_RECTANGLE_ARB)
+      return CAN_RENDER_NEVER;
+    else if (npot())
+      return CAN_RENDER_ONLY_IF_NPOT;
+  }
+
+  return CAN_RENDER_ALWAYS;
+}
+
+bool Texture::CanRender(const FeatureInfo* feature_info) const {
+  switch (can_render_condition_) {
+    case CAN_RENDER_ALWAYS:
+      return true;
+    case CAN_RENDER_NEVER:
+      return false;
+    case CAN_RENDER_ONLY_IF_NPOT:
+      break;
+  }
+  return feature_info->feature_flags().npot_ok;
 }
 
 void Texture::AddToSignature(
@@ -243,7 +300,8 @@
   return true;
 }
 
-void Texture::SetTarget(GLenum target, GLint max_levels) {
+void Texture::SetTarget(
+    const FeatureInfo* feature_info, GLenum target, GLint max_levels) {
   DCHECK_EQ(0u, target_);  // you can only set this once.
   target_ = target;
   size_t num_faces = (target == GL_TEXTURE_CUBE_MAP) ? 6 : 1;
@@ -260,6 +318,8 @@
   if (target == GL_TEXTURE_EXTERNAL_OES) {
     immutable_ = true;
   }
+  Update(feature_info);
+  UpdateCanRenderCondition();
 }
 
 bool Texture::CanGenerateMipmaps(
@@ -305,13 +365,7 @@
             level_infos_[GLTargetToFaceIndex(target)].size());
   Texture::LevelInfo& info =
       level_infos_[GLTargetToFaceIndex(target)][level];
-  if (!info.cleared) {
-    DCHECK_NE(0, num_uncleared_mips_);
-    --num_uncleared_mips_;
-  } else {
-    ++num_uncleared_mips_;
-  }
-  info.cleared = cleared;
+  UpdateMipCleared(&info, cleared);
   UpdateCleared();
 }
 
@@ -323,17 +377,61 @@
   const Texture::LevelInfo& first_face = level_infos_[0][0];
   int levels_needed = TextureManager::ComputeMipMapCount(
       first_face.width, first_face.height, first_face.depth);
-  cleared_ = true;
+  bool cleared = true;
   for (size_t ii = 0; ii < level_infos_.size(); ++ii) {
     for (GLint jj = 0; jj < levels_needed; ++jj) {
       const Texture::LevelInfo& info = level_infos_[ii][jj];
       if (info.width > 0 && info.height > 0 && info.depth > 0 &&
           !info.cleared) {
-        cleared_ = false;
-        return;
+        cleared = false;
+        break;
       }
     }
   }
+  UpdateSafeToRenderFrom(cleared);
+}
+
+void Texture::UpdateSafeToRenderFrom(bool cleared) {
+  if (cleared_ == cleared)
+    return;
+  cleared_ = cleared;
+  int delta = cleared ? -1 : +1;
+  for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
+    (*it)->manager()->UpdateSafeToRenderFrom(delta);
+}
+
+void Texture::UpdateMipCleared(LevelInfo* info, bool cleared) {
+  if (info->cleared == cleared)
+    return;
+  info->cleared = cleared;
+  int delta = cleared ? -1 : +1;
+  num_uncleared_mips_ += delta;
+  for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
+    (*it)->manager()->UpdateUnclearedMips(delta);
+}
+
+void Texture::UpdateCanRenderCondition() {
+  CanRenderCondition can_render_condition = GetCanRenderCondition();
+  if (can_render_condition_ == can_render_condition)
+    return;
+  for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
+    (*it)->manager()->UpdateCanRenderCondition(can_render_condition_,
+                                               can_render_condition);
+  can_render_condition_ = can_render_condition;
+}
+
+void Texture::IncAllFramebufferStateChangeCount() {
+  for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
+    (*it)->manager()->IncFramebufferStateChangeCount();
+}
+
+AsyncPixelTransferState* Texture::GetAsyncTransferState() const {
+  for (RefSet::const_iterator it = refs_.begin(); it != refs_.end(); ++it) {
+    AsyncPixelTransferState* state = (*it)->async_transfer_state();
+    if (state)
+      return state;
+  }
+  return NULL;
 }
 
 void Texture::SetLevelInfo(
@@ -374,17 +472,16 @@
       width, height, format, type, 4, &info.estimated_size, NULL, NULL);
   estimated_size_ += info.estimated_size;
 
-  if (!info.cleared) {
-    DCHECK_NE(0, num_uncleared_mips_);
-    --num_uncleared_mips_;
-  }
-  info.cleared = cleared;
-  if (!info.cleared) {
-    ++num_uncleared_mips_;
-  }
+  UpdateMipCleared(&info, cleared);
   max_level_set_ = std::max(max_level_set_, level);
   Update(feature_info);
   UpdateCleared();
+  UpdateCanRenderCondition();
+  if (IsAttachedToFramebuffer()) {
+    // TODO(gman): If textures tracked which framebuffers they were attached to
+    // we could just mark those framebuffers as not complete.
+    IncAllFramebufferStateChangeCount();
+  }
 }
 
 bool Texture::ValidForTexture(
@@ -479,9 +576,9 @@
       if (!feature_info->validators()->texture_pool.IsValid(param)) {
         return GL_INVALID_ENUM;
       }
-      manager_->GetMemTracker(pool_)->TrackMemFree(estimated_size());
+      GetMemTracker()->TrackMemFree(estimated_size());
       pool_ = param;
-      manager_->GetMemTracker(pool_)->TrackMemAlloc(estimated_size());
+      GetMemTracker()->TrackMemAlloc(estimated_size());
       break;
     case GL_TEXTURE_WRAP_S:
       if (!feature_info->validators()->texture_wrap_mode.IsValid(param)) {
@@ -512,12 +609,14 @@
   }
   Update(feature_info);
   UpdateCleared();
+  UpdateCanRenderCondition();
   return GL_NO_ERROR;
 }
 
 void Texture::Update(const FeatureInfo* feature_info) {
   // Update npot status.
-  npot_ = false;
+  // Assume GL_TEXTURE_EXTERNAL_OES textures are npot, all others
+  npot_ = target_ == GL_TEXTURE_EXTERNAL_OES;
 
   if (level_infos_.empty()) {
     texture_complete_ = false;
@@ -598,7 +697,7 @@
 
 bool Texture::ClearRenderableLevels(GLES2Decoder* decoder) {
   DCHECK(decoder);
-  if (SafeToRenderFrom()) {
+  if (cleared_) {
     return true;
   }
 
@@ -616,7 +715,7 @@
       }
     }
   }
-  cleared_ = true;
+  UpdateSafeToRenderFrom(true);
   return true;
 }
 
@@ -653,18 +752,13 @@
     return true;
   }
 
-  DCHECK_NE(0, num_uncleared_mips_);
-  --num_uncleared_mips_;
-
   // NOTE: It seems kind of gross to call back into the decoder for this
   // but only the decoder knows all the state (like unpack_alignment_) that's
   // needed to be able to call GL correctly.
-  info.cleared = decoder->ClearLevel(
+  bool cleared = decoder->ClearLevel(
       service_id_, target_, info.target, info.level, info.format, info.type,
       info.width, info.height, immutable_);
-  if (!info.cleared) {
-    ++num_uncleared_mips_;
-  }
+  UpdateMipCleared(&info, cleared);
   return info.cleared;
 }
 
@@ -683,6 +777,7 @@
   DCHECK_EQ(info.target, target);
   DCHECK_EQ(info.level, level);
   info.image = image;
+  UpdateCanRenderCondition();
 }
 
 gfx::GLImage* Texture::GetLevelImage(
@@ -698,6 +793,28 @@
   return 0;
 }
 
+
+TextureRef::TextureRef(TextureManager* manager, Texture* texture)
+    : manager_(manager),
+      texture_(texture) {
+  DCHECK(manager_);
+  DCHECK(texture_);
+  texture_->AddTextureRef(this);
+  manager_->StartTracking(this);
+}
+
+scoped_refptr<TextureRef> TextureRef::Create(TextureManager* manager,
+                                             GLuint service_id) {
+  return new TextureRef(manager, new Texture(service_id));
+}
+
+TextureRef::~TextureRef() {
+  manager_->StopTracking(this);
+  texture_->RemoveTextureRef(this, manager_->have_context_);
+  manager_ = NULL;
+}
+
+
 TextureManager::TextureManager(
     MemoryTracker* memory_tracker,
     FeatureInfo* feature_info,
@@ -708,6 +825,7 @@
       memory_tracker_unmanaged_(
           new MemoryTypeTracker(memory_tracker, MemoryTracker::kUnmanaged)),
       feature_info_(feature_info),
+      framebuffer_manager_(NULL),
       max_texture_size_(max_texture_size),
       max_cube_map_texture_size_(max_cube_map_texture_size),
       max_levels_(ComputeMipMapCount(max_texture_size,
@@ -749,7 +867,7 @@
   return true;
 }
 
-scoped_refptr<Texture>
+scoped_refptr<TextureRef>
     TextureManager::CreateDefaultAndBlackTextures(
         GLenum target,
         GLuint* black_texture) {
@@ -779,10 +897,7 @@
   }
   glBindTexture(target, 0);
 
-  // Since we are manually setting up these textures
-  // we need to manually manipulate some of the their bookkeeping.
-  ++num_unrenderable_textures_;
-  scoped_refptr<Texture> default_texture(new Texture(this, ids[1]));
+  scoped_refptr<TextureRef> default_texture(TextureRef::Create(this, ids[1]));
   SetTarget(default_texture, target);
   if (needs_faces) {
     for (int ii = 0; ii < GLES2Util::kNumFaces; ++ii) {
@@ -825,78 +940,45 @@
          (target != GL_TEXTURE_2D || (depth == 1));
 }
 
-void TextureManager::SetTarget(Texture* texture, GLenum target) {
-  DCHECK(texture);
-  if (!texture->CanRender(feature_info_)) {
-    DCHECK_NE(0, num_unrenderable_textures_);
-    --num_unrenderable_textures_;
-  }
-  texture->SetTarget(target, MaxLevelsForTarget(target));
-  if (!texture->CanRender(feature_info_)) {
-    ++num_unrenderable_textures_;
-  }
+void TextureManager::SetTarget(TextureRef* ref, GLenum target) {
+  DCHECK(ref);
+  ref->texture()->SetTarget(feature_info_, target, MaxLevelsForTarget(target));
 }
 
-void TextureManager::SetLevelCleared(Texture* texture,
+void TextureManager::SetStreamTexture(TextureRef* ref, bool stream_texture) {
+  DCHECK(ref);
+  ref->texture()->SetStreamTexture(stream_texture);
+}
+
+void TextureManager::SetLevelCleared(TextureRef* ref,
                                      GLenum target,
                                      GLint level,
                                      bool cleared) {
-  DCHECK(texture);
-  if (!texture->SafeToRenderFrom()) {
-    DCHECK_NE(0, num_unsafe_textures_);
-    --num_unsafe_textures_;
-  }
-  num_uncleared_mips_ -= texture->num_uncleared_mips();
-  DCHECK_GE(num_uncleared_mips_, 0);
-  texture->SetLevelCleared(target, level, cleared);
-  num_uncleared_mips_ += texture->num_uncleared_mips();
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
+  DCHECK(ref);
+  ref->texture()->SetLevelCleared(target, level, cleared);
 }
 
 bool TextureManager::ClearRenderableLevels(
-    GLES2Decoder* decoder,Texture* texture) {
-  DCHECK(texture);
-  if (texture->SafeToRenderFrom()) {
-    return true;
-  }
-  DCHECK_NE(0, num_unsafe_textures_);
-  --num_unsafe_textures_;
-  num_uncleared_mips_ -= texture->num_uncleared_mips();
-  DCHECK_GE(num_uncleared_mips_, 0);
-  bool result = texture->ClearRenderableLevels(decoder);
-  num_uncleared_mips_ += texture->num_uncleared_mips();
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
-  return result;
+    GLES2Decoder* decoder, TextureRef* ref) {
+  DCHECK(ref);
+  return ref->texture()->ClearRenderableLevels(decoder);
 }
 
 bool TextureManager::ClearTextureLevel(
-    GLES2Decoder* decoder,Texture* texture,
+    GLES2Decoder* decoder, TextureRef* ref,
     GLenum target, GLint level) {
-  DCHECK(texture);
+  DCHECK(ref);
+  Texture* texture = ref->texture();
   if (texture->num_uncleared_mips() == 0) {
     return true;
   }
-  num_uncleared_mips_ -= texture->num_uncleared_mips();
-  DCHECK_GE(num_uncleared_mips_, 0);
-  if (!texture->SafeToRenderFrom()) {
-    DCHECK_NE(0, num_unsafe_textures_);
-    --num_unsafe_textures_;
-  }
   bool result = texture->ClearLevel(decoder, target, level);
   texture->UpdateCleared();
-  num_uncleared_mips_ += texture->num_uncleared_mips();
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
   return result;
 }
 
 void TextureManager::SetLevelInfo(
-    Texture* texture,
+    TextureRef* ref,
     GLenum target,
     GLint level,
     GLenum internal_format,
@@ -907,34 +989,19 @@
     GLenum format,
     GLenum type,
     bool cleared) {
-  DCHECK(texture);
-  if (!texture->CanRender(feature_info_)) {
-    DCHECK_NE(0, num_unrenderable_textures_);
-    --num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    DCHECK_NE(0, num_unsafe_textures_);
-    --num_unsafe_textures_;
-  }
-  num_uncleared_mips_ -= texture->num_uncleared_mips();
-  DCHECK_GE(num_uncleared_mips_, 0);
+  DCHECK(ref);
+  Texture* texture = ref->texture();
 
-  GetMemTracker(texture->pool_)->TrackMemFree(texture->estimated_size());
+  texture->GetMemTracker()->TrackMemFree(texture->estimated_size());
   texture->SetLevelInfo(
       feature_info_, target, level, internal_format, width, height, depth,
       border, format, type, cleared);
-  GetMemTracker(texture->pool_)->TrackMemAlloc(texture->estimated_size());
-
-  num_uncleared_mips_ += texture->num_uncleared_mips();
-  if (!texture->CanRender(feature_info_)) {
-    ++num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
+  texture->GetMemTracker()->TrackMemAlloc(texture->estimated_size());
 }
 
-TextureDefinition* TextureManager::Save(Texture* texture) {
+TextureDefinition* TextureManager::Save(TextureRef* ref) {
+  DCHECK(ref);
+  Texture* texture = ref->texture();
   DCHECK(texture->owned_);
 
   if (texture->IsAttachedToFramebuffer())
@@ -960,7 +1027,7 @@
                                        level_info.type,
                                        level_info.cleared));
 
-      SetLevelInfo(texture,
+      SetLevelInfo(ref,
                    target,
                    level,
                    GL_RGBA,
@@ -999,8 +1066,10 @@
 bool TextureManager::Restore(
     const char* function_name,
     GLES2Decoder* decoder,
-    Texture* texture,
+    TextureRef* ref,
     TextureDefinition* definition) {
+  DCHECK(ref);
+  Texture* texture = ref->texture();
   DCHECK(texture->owned_);
 
   scoped_ptr<TextureDefinition> scoped_definition(definition);
@@ -1028,7 +1097,7 @@
       const TextureDefinition::LevelInfo& level_info =
           level <= new_max_level ? definition->level_infos()[face][level]
                                  : TextureDefinition::LevelInfo();
-      SetLevelInfo(texture,
+      SetLevelInfo(ref,
                    target,
                    level,
                    level_info.internal_format,
@@ -1048,19 +1117,18 @@
   glBindTexture(texture->target(), texture->service_id());
   texture->SetImmutable(definition->immutable());
   texture->SetStreamTexture(definition->stream_texture());
-
   ErrorState* error_state = decoder->GetErrorState();
-  SetParameter(function_name, error_state, texture, GL_TEXTURE_MIN_FILTER,
+  SetParameter(function_name, error_state, ref, GL_TEXTURE_MIN_FILTER,
                definition->min_filter());
-  SetParameter(function_name, error_state, texture, GL_TEXTURE_MAG_FILTER,
+  SetParameter(function_name, error_state, ref, GL_TEXTURE_MAG_FILTER,
                definition->mag_filter());
-  SetParameter(function_name, error_state, texture, GL_TEXTURE_WRAP_S,
+  SetParameter(function_name, error_state, ref, GL_TEXTURE_WRAP_S,
                definition->wrap_s());
-  SetParameter(function_name, error_state, texture, GL_TEXTURE_WRAP_T,
+  SetParameter(function_name, error_state, ref, GL_TEXTURE_WRAP_T,
                definition->wrap_t());
   if (feature_info_->validators()->texture_parameter.IsValid(
       GL_TEXTURE_USAGE_ANGLE)) {
-    SetParameter(function_name, error_state, texture, GL_TEXTURE_USAGE_ANGLE,
+    SetParameter(function_name, error_state, ref, GL_TEXTURE_USAGE_ANGLE,
                  definition->usage());
   }
 
@@ -1069,17 +1137,10 @@
 
 void TextureManager::SetParameter(
     const char* function_name, ErrorState* error_state,
-    Texture* texture, GLenum pname, GLint param) {
+    TextureRef* ref, GLenum pname, GLint param) {
   DCHECK(error_state);
-  DCHECK(texture);
-  if (!texture->CanRender(feature_info_)) {
-    DCHECK_NE(0, num_unrenderable_textures_);
-    --num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    DCHECK_NE(0, num_unsafe_textures_);
-    --num_unsafe_textures_;
-  }
+  DCHECK(ref);
+  Texture* texture = ref->texture();
   GLenum result = texture->SetParameter(feature_info_, pname, param);
   if (result != GL_NO_ERROR) {
     if (result == GL_INVALID_ENUM) {
@@ -1096,59 +1157,28 @@
       glTexParameteri(texture->target(), pname, param);
     }
   }
-
-  if (!texture->CanRender(feature_info_)) {
-    ++num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
 }
 
-bool TextureManager::MarkMipmapsGenerated(Texture* texture) {
-  DCHECK(texture);
-  if (!texture->CanRender(feature_info_)) {
-    DCHECK_NE(0, num_unrenderable_textures_);
-    --num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    DCHECK_NE(0, num_unsafe_textures_);
-    --num_unsafe_textures_;
-  }
-  num_uncleared_mips_ -= texture->num_uncleared_mips();
-  DCHECK_GE(num_uncleared_mips_, 0);
-  GetMemTracker(texture->pool_)->TrackMemFree(texture->estimated_size());
+bool TextureManager::MarkMipmapsGenerated(TextureRef* ref) {
+  DCHECK(ref);
+  Texture* texture = ref->texture();
+  texture->GetMemTracker()->TrackMemFree(texture->estimated_size());
   bool result = texture->MarkMipmapsGenerated(feature_info_);
-  GetMemTracker(texture->pool_)->TrackMemAlloc(texture->estimated_size());
-
-  num_uncleared_mips_ += texture->num_uncleared_mips();
-  if (!texture->CanRender(feature_info_)) {
-    ++num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
+  texture->GetMemTracker()->TrackMemAlloc(texture->estimated_size());
   return result;
 }
 
-Texture* TextureManager::CreateTexture(
+TextureRef* TextureManager::CreateTexture(
     GLuint client_id, GLuint service_id) {
   DCHECK_NE(0u, service_id);
-  scoped_refptr<Texture> texture(new Texture(this, service_id));
+  scoped_refptr<TextureRef> ref(TextureRef::Create(this, service_id));
   std::pair<TextureMap::iterator, bool> result =
-      textures_.insert(std::make_pair(client_id, texture));
+      textures_.insert(std::make_pair(client_id, ref));
   DCHECK(result.second);
-  if (!texture->CanRender(feature_info_)) {
-    ++num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
-  num_uncleared_mips_ += texture->num_uncleared_mips();
-  return texture.get();
+  return ref.get();
 }
 
-Texture* TextureManager::GetTexture(
+TextureRef* TextureManager::GetTexture(
     GLuint client_id) const {
   TextureMap::const_iterator it = textures_.find(client_id);
   return it != textures_.end() ? it->second : NULL;
@@ -1157,17 +1187,22 @@
 void TextureManager::RemoveTexture(GLuint client_id) {
   TextureMap::iterator it = textures_.find(client_id);
   if (it != textures_.end()) {
-    Texture* texture = it->second;
-    texture->MarkAsDeleted();
     textures_.erase(it);
   }
 }
 
-void TextureManager::StartTracking(Texture* /* texture */) {
+void TextureManager::StartTracking(TextureRef* ref) {
+  Texture* texture = ref->texture();
   ++texture_count_;
+  num_uncleared_mips_ += texture->num_uncleared_mips();
+  if (!texture->SafeToRenderFrom())
+    ++num_unsafe_textures_;
+  if (!texture->CanRender(feature_info_))
+    ++num_unrenderable_textures_;
 }
 
-void TextureManager::StopTracking(Texture* texture) {
+void TextureManager::StopTracking(TextureRef* ref) {
+  Texture* texture = ref->texture();
   --texture_count_;
   if (!texture->CanRender(feature_info_)) {
     DCHECK_NE(0, num_unrenderable_textures_);
@@ -1179,7 +1214,6 @@
   }
   num_uncleared_mips_ -= texture->num_uncleared_mips();
   DCHECK_GE(num_uncleared_mips_, 0);
-  GetMemTracker(texture->pool_)->TrackMemFree(texture->estimated_size());
 }
 
 MemoryTypeTracker* TextureManager::GetMemTracker(GLenum tracking_pool) {
@@ -1201,7 +1235,7 @@
   // This doesn't need to be fast. It's only used during slow queries.
   for (TextureMap::const_iterator it = textures_.begin();
        it != textures_.end(); ++it) {
-    if (it->second->service_id() == service_id) {
+    if (it->second->texture()->service_id() == service_id) {
       *client_id = it->first;
       return true;
     }
@@ -1215,34 +1249,51 @@
 }
 
 void TextureManager::SetLevelImage(
-    Texture* texture,
+    TextureRef* ref,
     GLenum target,
     GLint level,
     gfx::GLImage* image) {
-  DCHECK(texture);
-  if (!texture->CanRender(feature_info_)) {
-    DCHECK_NE(0, num_unrenderable_textures_);
-    --num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    DCHECK_NE(0, num_unsafe_textures_);
-    --num_unsafe_textures_;
-  }
-  texture->SetLevelImage(feature_info_, target, level, image);
-  if (!texture->CanRender(feature_info_)) {
-    ++num_unrenderable_textures_;
-  }
-  if (!texture->SafeToRenderFrom()) {
-    ++num_unsafe_textures_;
-  }
+  DCHECK(ref);
+  ref->texture()->SetLevelImage(feature_info_, target, level, image);
 }
 
 void TextureManager::AddToSignature(
-    Texture* texture,
+    TextureRef* ref,
     GLenum target,
     GLint level,
     std::string* signature) const {
-  texture->AddToSignature(feature_info_.get(), target, level, signature);
+  ref->texture()->AddToSignature(feature_info_.get(), target, level, signature);
+}
+
+void TextureManager::UpdateSafeToRenderFrom(int delta) {
+  num_unsafe_textures_ += delta;
+  DCHECK_GE(num_unsafe_textures_, 0);
+}
+
+void TextureManager::UpdateUnclearedMips(int delta) {
+  num_uncleared_mips_ += delta;
+  DCHECK_GE(num_uncleared_mips_, 0);
+}
+
+void TextureManager::UpdateCanRenderCondition(
+    Texture::CanRenderCondition old_condition,
+    Texture::CanRenderCondition new_condition) {
+  if (old_condition == Texture::CAN_RENDER_NEVER ||
+      (old_condition == Texture::CAN_RENDER_ONLY_IF_NPOT &&
+       !feature_info_->feature_flags().npot_ok)) {
+    DCHECK_GT(num_unrenderable_textures_, 0);
+    --num_unrenderable_textures_;
+  }
+  if (new_condition == Texture::CAN_RENDER_NEVER ||
+      (new_condition == Texture::CAN_RENDER_ONLY_IF_NPOT &&
+       !feature_info_->feature_flags().npot_ok))
+    ++num_unrenderable_textures_;
+}
+
+void TextureManager::IncFramebufferStateChangeCount() {
+  if (framebuffer_manager_)
+    framebuffer_manager_->IncFramebufferStateChangeCount();
+
 }
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 7293f27..af5eb0c 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -6,6 +6,7 @@
 #define GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_
 
 #include <list>
+#include <set>
 #include <string>
 #include <vector>
 #include "base/basictypes.h"
@@ -25,13 +26,17 @@
 class Display;
 class ErrorState;
 class FeatureInfo;
+class FramebufferManager;
 class TextureDefinition;
 class TextureManager;
+class TextureRef;
 
 // Info about Textures currently in the system.
-class GPU_EXPORT Texture : public base::RefCounted<Texture> {
+// This class wraps a real GL texture, keeping track of its meta-data. It is
+// jointly owned by possibly multiple TextureRef.
+class GPU_EXPORT Texture {
  public:
-  Texture(TextureManager* manager, GLuint service_id);
+  explicit Texture(GLuint service_id);
 
   GLenum min_filter() const {
     return min_filter_;
@@ -102,10 +107,6 @@
   // does not exist.
   gfx::GLImage* GetLevelImage(GLint target, GLint level) const;
 
-  bool IsDeleted() const {
-    return deleted_;
-  }
-
   // Returns true of the given dimensions are inside the dimensions of the
   // level and if the format and type match the level.
   bool ValidForTexture(
@@ -119,7 +120,7 @@
       GLenum type) const;
 
   bool IsValid() const {
-    return target() && !IsDeleted();
+    return !!target();
   }
 
   void SetNotOwned() {
@@ -139,30 +140,24 @@
     --framebuffer_attachment_count_;
   }
 
-  void SetStreamTexture(bool stream_texture) {
-    stream_texture_ = stream_texture;
-  }
-
-  bool IsStreamTexture() {
+  bool IsStreamTexture() const {
     return stream_texture_;
   }
 
-  gpu::AsyncPixelTransferState* GetAsyncTransferState() const {
-    return async_transfer_state_.get();
-  }
-  void SetAsyncTransferState(scoped_ptr<gpu::AsyncPixelTransferState> state) {
-    async_transfer_state_ = state.Pass();
-  }
+  // Gets the async transfer state for this texture. Note: the transfer state is
+  // owned by a single TextureRef.
+  AsyncPixelTransferState* GetAsyncTransferState() const;
+
   bool AsyncTransferIsInProgress() {
-    return async_transfer_state_ &&
-        async_transfer_state_->TransferIsInProgress();
+    AsyncPixelTransferState* state = GetAsyncTransferState();
+    return state && state->TransferIsInProgress();
   }
 
   void SetImmutable(bool immutable) {
     immutable_ = immutable;
   }
 
-  bool IsImmutable() {
+  bool IsImmutable() const {
     return immutable_;
   }
 
@@ -170,16 +165,31 @@
   bool IsLevelCleared(GLenum target, GLint level) const;
 
   // Whether the texture has been defined
-  bool IsDefined() {
+  bool IsDefined() const {
     return estimated_size() > 0;
   }
 
  private:
   friend class TextureManager;
+  friend class TextureRef;
   friend class TextureTestHelper;
-  friend class base::RefCounted<Texture>;
 
   ~Texture();
+  void AddTextureRef(TextureRef* ref);
+  void RemoveTextureRef(TextureRef* ref, bool have_context);
+  MemoryTypeTracker* GetMemTracker();
+
+  // Condition on which this texture is renderable. Can be ONLY_IF_NPOT if it
+  // depends on context support for non-power-of-two textures (i.e. will be
+  // renderable if NPOT support is in the context, otherwise not, e.g. texture
+  // with a NPOT level). ALWAYS means it doesn't depend on context features
+  // (e.g. complete POT), NEVER means it's not renderable regardless (e.g.
+  // incomplete).
+  enum CanRenderCondition {
+    CAN_RENDER_ALWAYS,
+    CAN_RENDER_NEVER,
+    CAN_RENDER_ONLY_IF_NPOT
+  };
 
   struct LevelInfo {
     LevelInfo();
@@ -232,6 +242,11 @@
     return npot_;
   }
 
+  void SetStreamTexture(bool stream_texture) {
+    stream_texture_ = stream_texture;
+    UpdateCanRenderCondition();
+  }
+
   // Marks a particular level as cleared or uncleared.
   void SetLevelCleared(GLenum target, GLint level, bool cleared);
 
@@ -255,10 +270,6 @@
   // Makes each of the mip levels as though they were generated.
   bool MarkMipmapsGenerated(const FeatureInfo* feature_info);
 
-  void MarkAsDeleted() {
-    deleted_ = true;
-  }
-
   bool NeedsMips() const {
     return min_filter_ != GL_NEAREST && min_filter_ != GL_LINEAR;
   }
@@ -275,7 +286,8 @@
   //   target: GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP or
   //           GL_TEXTURE_EXTERNAL_OES or GL_TEXTURE_RECTANGLE_ARB
   //   max_levels: The maximum levels this type of target can have.
-  void SetTarget(GLenum target, GLint max_levels);
+  void SetTarget(
+      const FeatureInfo* feature_info, GLenum target, GLint max_levels);
 
   // Update info about this texture.
   void Update(const FeatureInfo* feature_info);
@@ -292,18 +304,39 @@
       const FeatureInfo* feature_info,
       GLenum target, GLint level, std::string* signature) const;
 
+  // Updates the unsafe textures count in all the managers referencing this
+  // texture.
+  void UpdateSafeToRenderFrom(bool cleared);
+
+  // Updates the uncleared mip count in all the managers referencing this
+  // texture.
+  void UpdateMipCleared(LevelInfo* info, bool cleared);
+
+  // Computes the CanRenderCondition flag.
+  CanRenderCondition GetCanRenderCondition() const;
+
+  // Updates the unrenderable texture count in all the managers referencing this
+  // texture.
+  void UpdateCanRenderCondition();
+
+  // Increment the framebuffer state change count in all the managers
+  // referencing this texture.
+  void IncAllFramebufferStateChangeCount();
+
   // Info about each face and level of texture.
   std::vector<std::vector<LevelInfo> > level_infos_;
 
-  // The texture manager that manages this Texture.
-  TextureManager* manager_;
+  // The texture refs that point to this Texture.
+  typedef std::set<TextureRef*> RefSet;
+  RefSet refs_;
+
+  // The single TextureRef that accounts for memory for this texture. Must be
+  // one of refs_.
+  TextureRef* memory_tracking_ref_;
 
   // The id of the texure
   GLuint service_id_;
 
-  // Whether this texture has been deleted.
-  bool deleted_;
-
   // Whether all renderable mips of this texture have been cleared.
   bool cleared_;
 
@@ -345,9 +378,6 @@
   // Whether this is a special streaming texture.
   bool stream_texture_;
 
-  // State to facilitate async transfers on this texture.
-  scoped_ptr<gpu::AsyncPixelTransferState> async_transfer_state_;
-
   // Whether the texture is immutable and no further changes to the format
   // or dimensions of the texture object can be made.
   bool immutable_;
@@ -355,9 +385,55 @@
   // Size in bytes this texture is assumed to take in memory.
   uint32 estimated_size_;
 
+  // Cache of the computed CanRenderCondition flag.
+  CanRenderCondition can_render_condition_;
+
   DISALLOW_COPY_AND_ASSIGN(Texture);
 };
 
+// This class represents a texture in a client context group. It's mostly 1:1
+// with a client id, though it can outlive the client id if it's still bound to
+// a FBO or another context when destroyed.
+// Multiple TextureRef can point to the same texture with cross-context sharing.
+class GPU_EXPORT TextureRef : public base::RefCounted<TextureRef> {
+ public:
+  TextureRef(TextureManager* manager, Texture* texture);
+  static scoped_refptr<TextureRef> Create(TextureManager* manager,
+                                          GLuint service_id);
+  const Texture* texture() const { return texture_; }
+  Texture* texture() { return texture_; }
+  GLuint service_id() const { return texture_->service_id(); }
+
+  // Sets the async transfer state for this texture. Only a single TextureRef
+  // can set this on a given texture at any time.
+  // NOTE: this should be per-context rather than per-texture. crbug.com/240504
+  void SetAsyncTransferState(
+      scoped_ptr<AsyncPixelTransferState> state) {
+    DCHECK(!state || !texture_->GetAsyncTransferState());
+    async_transfer_state_ = state.Pass();
+  }
+
+ private:
+  friend class base::RefCounted<TextureRef>;
+  friend class Texture;
+  friend class TextureManager;
+
+  ~TextureRef();
+  const TextureManager* manager() const { return manager_; }
+  TextureManager* manager() { return manager_; }
+  AsyncPixelTransferState* async_transfer_state() const {
+    return async_transfer_state_.get();
+  }
+
+  TextureManager* manager_;
+  Texture* texture_;
+
+  // State to facilitate async transfers on this texture.
+  scoped_ptr<AsyncPixelTransferState> async_transfer_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(TextureRef);
+};
+
 // This class keeps track of the textures and their sizes so we can do NPOT and
 // texture complete checking.
 //
@@ -379,6 +455,10 @@
                  GLsizei max_cube_map_texture_size);
   ~TextureManager();
 
+  void set_framebuffer_manager(FramebufferManager* manager) {
+    framebuffer_manager_ = manager;
+  }
+
   // Init the texture manager.
   bool Initialize();
 
@@ -419,13 +499,13 @@
 
   // True if this texture meets all the GLES2 criteria for rendering.
   // See section 3.8.2 of the GLES2 spec.
-  bool CanRender(const Texture* texture) const {
-    return texture->CanRender(feature_info_);
+  bool CanRender(const TextureRef* ref) const {
+    return ref->texture()->CanRender(feature_info_);
   }
 
   // Returns true if mipmaps can be generated by GL.
-  bool CanGenerateMipmaps(const Texture* texture) const {
-    return texture->CanGenerateMipmaps(feature_info_);
+  bool CanGenerateMipmaps(const TextureRef* ref) const {
+    return ref->texture()->CanGenerateMipmaps(feature_info_);
   }
 
   // Sets the Texture's target
@@ -433,12 +513,15 @@
   //   target: GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP
   //   max_levels: The maximum levels this type of target can have.
   void SetTarget(
-      Texture* texture,
+      TextureRef* ref,
       GLenum target);
 
+  // Marks a texture as a stream texture.
+  void SetStreamTexture(TextureRef* ref, bool stream_texture);
+
   // Set the info for a particular level in a TexureInfo.
   void SetLevelInfo(
-      Texture* texture,
+      TextureRef* ref,
       GLenum target,
       GLint level,
       GLenum internal_format,
@@ -451,27 +534,27 @@
       bool cleared);
 
   // Adapter to call above function.
-  void SetLevelInfoFromParams(Texture* texture,
+  void SetLevelInfoFromParams(TextureRef* ref,
                               const gpu::AsyncTexImage2DParams& params) {
     SetLevelInfo(
-        texture, params.target, params.level, params.internal_format,
+        ref, params.target, params.level, params.internal_format,
         params.width, params.height, 1 /* depth */,
         params.border, params.format,
         params.type, true /* cleared */ );
   }
 
   // Save the texture definition and leave it undefined.
-  TextureDefinition* Save(Texture* texture);
+  TextureDefinition* Save(TextureRef* ref);
 
   // Redefine all the levels from the texture definition.
   bool Restore(
       const char* function_name,
       GLES2Decoder* decoder,
-      Texture* texture,
+      TextureRef* ref,
       TextureDefinition* definition);
 
   // Sets a mip as cleared.
-  void SetLevelCleared(Texture* texture, GLenum target,
+  void SetLevelCleared(TextureRef* ref, GLenum target,
                        GLint level, bool cleared);
 
   // Sets a texture parameter of a Texture
@@ -479,24 +562,24 @@
   // TODO(gman): Expand to SetParameteri,f,iv,fv
   void SetParameter(
       const char* function_name, ErrorState* error_state,
-      Texture* texture, GLenum pname, GLint param);
+      TextureRef* ref, GLenum pname, GLint param);
 
   // Makes each of the mip levels as though they were generated.
   // Returns false if that's not allowed for the given texture.
-  bool MarkMipmapsGenerated(Texture* texture);
+  bool MarkMipmapsGenerated(TextureRef* ref);
 
   // Clears any uncleared renderable levels.
-  bool ClearRenderableLevels(GLES2Decoder* decoder, Texture* texture);
+  bool ClearRenderableLevels(GLES2Decoder* decoder, TextureRef* ref);
 
   // Clear a specific level.
   bool ClearTextureLevel(
-      GLES2Decoder* decoder,Texture* texture, GLenum target, GLint level);
+      GLES2Decoder* decoder, TextureRef* ref, GLenum target, GLint level);
 
   // Creates a new texture info.
-  Texture* CreateTexture(GLuint client_id, GLuint service_id);
+  TextureRef* CreateTexture(GLuint client_id, GLuint service_id);
 
   // Gets the texture info for the given texture.
-  Texture* GetTexture(GLuint client_id) const;
+  TextureRef* GetTexture(GLuint client_id) const;
 
   // Removes a texture info.
   void RemoveTexture(GLuint client_id);
@@ -504,7 +587,7 @@
   // Gets a client id for a given service id.
   bool GetClientId(GLuint service_id, GLuint* client_id) const;
 
-  Texture* GetDefaultTextureInfo(GLenum target) {
+  TextureRef* GetDefaultTextureInfo(GLenum target) {
     switch (target) {
       case GL_TEXTURE_2D:
         return default_textures_[kTexture2D];
@@ -555,27 +638,34 @@
   }
 
   void SetLevelImage(
-      Texture* texture,
+      TextureRef* ref,
       GLenum target,
       GLint level,
       gfx::GLImage* image);
 
   void AddToSignature(
-      Texture* texture,
+      TextureRef* ref,
       GLenum target,
       GLint level,
       std::string* signature) const;
 
  private:
   friend class Texture;
+  friend class TextureRef;
 
   // Helper for Initialize().
-  scoped_refptr<Texture> CreateDefaultAndBlackTextures(
+  scoped_refptr<TextureRef> CreateDefaultAndBlackTextures(
       GLenum target,
       GLuint* black_texture);
 
-  void StartTracking(Texture* texture);
-  void StopTracking(Texture* texture);
+  void StartTracking(TextureRef* texture);
+  void StopTracking(TextureRef* texture);
+
+  void UpdateSafeToRenderFrom(int delta);
+  void UpdateUnclearedMips(int delta);
+  void UpdateCanRenderCondition(Texture::CanRenderCondition old_condition,
+                                Texture::CanRenderCondition new_condition);
+  void IncFramebufferStateChangeCount();
 
   MemoryTypeTracker* GetMemTracker(GLenum texture_pool);
   scoped_ptr<MemoryTypeTracker> memory_tracker_managed_;
@@ -583,8 +673,10 @@
 
   scoped_refptr<FeatureInfo> feature_info_;
 
+  FramebufferManager* framebuffer_manager_;
+
   // Info for each texture in the system.
-  typedef base::hash_map<GLuint, scoped_refptr<Texture> > TextureMap;
+  typedef base::hash_map<GLuint, scoped_refptr<TextureRef> > TextureMap;
   TextureMap textures_;
 
   GLsizei max_texture_size_;
@@ -608,7 +700,7 @@
   GLuint black_texture_ids_[kNumDefaultTextures];
 
   // The default textures for each target (texture name = 0)
-  scoped_refptr<Texture> default_textures_[kNumDefaultTextures];
+  scoped_refptr<TextureRef> default_textures_[kNumDefaultTextures];
 
   DISALLOW_COPY_AND_ASSIGN(TextureManager);
 };
diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc
index bef870b..c66ffa6 100644
--- a/gpu/command_buffer/service/texture_manager_unittest.cc
+++ b/gpu/command_buffer/service/texture_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "gpu/command_buffer/service/error_state_mock.h"
 #include "gpu/command_buffer/service/feature_info.h"
+#include "gpu/command_buffer/service/framebuffer_manager.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/mocks.h"
@@ -75,10 +76,10 @@
   }
 
   void SetParameter(
-      Texture* texture, GLenum pname, GLint value, GLenum error) {
+      TextureRef* texture_ref, GLenum pname, GLint value, GLenum error) {
     TestHelper::SetTexParameterWithExpectations(
         gl_.get(), error_state_.get(), manager_.get(),
-        texture, pname, value, error);
+        texture_ref, pname, value, error);
   }
 
   // Use StrictMock to make 100% sure we know how GL will be called.
@@ -108,7 +109,7 @@
   // Check we can create texture.
   manager_->CreateTexture(kClient1Id, kService1Id);
   // Check texture got created.
-  Texture* texture = manager_->GetTexture(kClient1Id);
+  TextureRef* texture = manager_->GetTexture(kClient1Id);
   ASSERT_TRUE(texture != NULL);
   EXPECT_EQ(kService1Id, texture->service_id());
   GLuint client_id = 0;
@@ -133,30 +134,31 @@
   // Check we can create texture.
   manager_->CreateTexture(kClient1Id, kService1Id);
   // Check texture got created.
-  Texture* texture = manager_->GetTexture(kClient1Id);
-  manager_->SetTarget(texture, GL_TEXTURE_2D);
-  ASSERT_TRUE(texture != NULL);
-  SetParameter(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR);
+  TextureRef* texture_ref = manager_->GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
+  manager_->SetTarget(texture_ref, GL_TEXTURE_2D);
+  SetParameter(texture_ref, GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR);
   EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter());
-  SetParameter(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
+  SetParameter(texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
   EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->mag_filter());
-  SetParameter(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
+  SetParameter(texture_ref, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
   EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_s());
-  SetParameter(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
+  SetParameter(texture_ref, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
   EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_t());
-  SetParameter(texture, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1, GL_NO_ERROR);
-  SetParameter(texture, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2, GL_NO_ERROR);
+  SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1, GL_NO_ERROR);
+  SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2, GL_NO_ERROR);
   SetParameter(
-      texture, GL_TEXTURE_MIN_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM);
+      texture_ref, GL_TEXTURE_MIN_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM);
   EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter());
   SetParameter(
-      texture, GL_TEXTURE_MAG_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM);
+      texture_ref, GL_TEXTURE_MAG_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM);
   EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter());
-  SetParameter(texture, GL_TEXTURE_WRAP_S, GL_NEAREST, GL_INVALID_ENUM);
+  SetParameter(texture_ref, GL_TEXTURE_WRAP_S, GL_NEAREST, GL_INVALID_ENUM);
   EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_s());
-  SetParameter(texture, GL_TEXTURE_WRAP_T, GL_NEAREST, GL_INVALID_ENUM);
+  SetParameter(texture_ref, GL_TEXTURE_WRAP_T, GL_NEAREST, GL_INVALID_ENUM);
   EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_t());
-  SetParameter(texture, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0, GL_INVALID_VALUE);
+  SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0, GL_INVALID_VALUE);
 }
 
 TEST_F(TextureManagerTest, TextureUsageExt) {
@@ -170,13 +172,13 @@
   // Check we can create texture.
   manager.CreateTexture(kClient1Id, kService1Id);
   // Check texture got created.
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
   TestHelper::SetTexParameterWithExpectations(
-      gl_.get(), error_state_.get(), &manager, texture,
+      gl_.get(), error_state_.get(), &manager, texture_ref,
       GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE,GL_NO_ERROR);
   EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_ATTACHMENT_ANGLE),
-            texture->usage());
+            texture_ref->texture()->usage());
   manager.Destroy(false);
 }
 
@@ -190,7 +192,7 @@
   // Check we can create texture.
   manager.CreateTexture(kClient1Id, kService1Id);
   // Check texture got created.
-  Texture* texture = manager.GetTexture(kClient1Id);
+  TextureRef* texture = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture != NULL);
   EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService1Id)))
       .Times(1)
@@ -210,12 +212,12 @@
       NULL, feature_info_.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.Initialize();
   // Check we can create texture.
-  Texture* created_texture =
+  TextureRef* created_texture =
       manager.CreateTexture(kClient1Id, kService1Id);
-  created_texture->SetNotOwned();
+  created_texture->texture()->SetNotOwned();
 
   // Check texture got created.
-  Texture* texture = manager.GetTexture(kClient1Id);
+  TextureRef* texture = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture != NULL);
 
   // Check that it is not freed if it is not owned.
@@ -365,7 +367,7 @@
       : feature_info_(new FeatureInfo()) {
   }
   virtual ~TextureTestBase() {
-    texture_ = NULL;
+    texture_ref_ = NULL;
   }
 
  protected:
@@ -385,23 +387,23 @@
     decoder_.reset(new ::testing::StrictMock<gles2::MockGLES2Decoder>());
     error_state_.reset(new ::testing::StrictMock<gles2::MockErrorState>());
     manager_->CreateTexture(kClient1Id, kService1Id);
-    texture_ = manager_->GetTexture(kClient1Id);
-    ASSERT_TRUE(texture_.get() != NULL);
+    texture_ref_ = manager_->GetTexture(kClient1Id);
+    ASSERT_TRUE(texture_ref_.get() != NULL);
   }
 
   virtual void TearDown() {
-    if (texture_.get()) {
+    if (texture_ref_.get()) {
       GLuint client_id = 0;
-      // If it's not in the manager then setting texture_ to NULL will
+      // If it's not in the manager then setting texture_ref_ to NULL will
       // delete the texture.
-      if (!manager_->GetClientId(texture_->service_id(), &client_id)) {
+      if (!manager_->GetClientId(texture_ref_->service_id(), &client_id)) {
         // Check that it gets deleted when the last reference is released.
         EXPECT_CALL(*gl_,
-            DeleteTextures(1, ::testing::Pointee(texture_->service_id())))
+            DeleteTextures(1, ::testing::Pointee(texture_ref_->service_id())))
             .Times(1)
             .RetiresOnSaturation();
       }
-      texture_ = NULL;
+      texture_ref_ = NULL;
     }
     manager_->Destroy(false);
     manager_.reset();
@@ -410,10 +412,10 @@
   }
 
   void SetParameter(
-      Texture* texture, GLenum pname, GLint value, GLenum error) {
+      TextureRef* texture_ref, GLenum pname, GLint value, GLenum error) {
     TestHelper::SetTexParameterWithExpectations(
         gl_.get(), error_state_.get(), manager_.get(),
-        texture, pname, value, error);
+        texture_ref, pname, value, error);
   }
 
   scoped_ptr<MockGLES2Decoder> decoder_;
@@ -422,7 +424,7 @@
   scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_;
   scoped_refptr<FeatureInfo> feature_info_;
   scoped_ptr<TextureManager> manager_;
-  scoped_refptr<Texture> texture_;
+  scoped_refptr<TextureRef> texture_ref_;
 };
 
 class TextureTest : public TextureTestBase {
@@ -449,65 +451,81 @@
         .RetiresOnSaturation() \
 
 TEST_F(TextureTest, Basic) {
-  EXPECT_EQ(0u, texture_->target());
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
-  EXPECT_FALSE(manager_->CanRender(texture_));
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
-  EXPECT_FALSE(texture_->IsImmutable());
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(0u, texture->target());
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_EQ(0, texture->num_uncleared_mips());
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
+  EXPECT_TRUE(texture->SafeToRenderFrom());
+  EXPECT_FALSE(texture->IsImmutable());
   EXPECT_EQ(static_cast<GLenum>(GL_NEAREST_MIPMAP_LINEAR),
-            texture_->min_filter());
-  EXPECT_EQ(static_cast<GLenum>(GL_LINEAR), texture_->mag_filter());
-  EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture_->wrap_s());
-  EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture_->wrap_t());
-  EXPECT_TRUE(manager_->HaveUnrenderableTextures());
+            texture->min_filter());
+  EXPECT_EQ(static_cast<GLenum>(GL_LINEAR), texture->mag_filter());
+  EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture->wrap_s());
+  EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture->wrap_t());
+  EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
-  EXPECT_EQ(0u, texture_->estimated_size());
+  EXPECT_EQ(0u, texture->estimated_size());
 }
 
 TEST_F(TextureTest, SetTargetTexture2D) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
-  EXPECT_FALSE(texture_->IsImmutable());
+  Texture* texture = texture_ref_->texture();
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
+  EXPECT_TRUE(texture->SafeToRenderFrom());
+  EXPECT_FALSE(texture->IsImmutable());
 }
 
 TEST_F(TextureTest, SetTargetTextureExternalOES) {
-  manager_->SetTarget(texture_, GL_TEXTURE_EXTERNAL_OES);
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_TRUE(manager_->CanRender(texture_));
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
-  EXPECT_TRUE(texture_->IsImmutable());
+  Texture* texture = texture_ref_->texture();
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_EXTERNAL_OES);
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_TRUE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
+  EXPECT_TRUE(texture->SafeToRenderFrom());
+  EXPECT_TRUE(texture->IsImmutable());
+  manager_->SetStreamTexture(texture_ref_, true);
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
+}
+
+TEST_F(TextureTest, ZeroSizeCanNotRender) {
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
+  manager_->SetLevelInfo(texture_ref_,
+      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
+  manager_->SetLevelInfo(texture_ref_,
+      GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
 }
 
 TEST_F(TextureTest, EstimatedSize) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_EQ(8u * 4u * 4u, texture_->estimated_size());
-  manager_->SetLevelInfo(texture_,
+  EXPECT_EQ(8u * 4u * 4u, texture_ref_->texture()->estimated_size());
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 2, GL_RGBA, 8, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_EQ(8u * 4u * 4u * 2u, texture_->estimated_size());
+  EXPECT_EQ(8u * 4u * 4u * 2u, texture_ref_->texture()->estimated_size());
 }
 
 TEST_F(TextureMemoryTrackerTest, EstimatedSize) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, 128, MemoryTracker::kUnmanaged);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   EXPECT_MEMORY_ALLOCATION_CHANGE(128, 0, MemoryTracker::kUnmanaged);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, 256, MemoryTracker::kUnmanaged);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 2, GL_RGBA, 8, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   // Add expectation for texture deletion.
   EXPECT_MEMORY_ALLOCATION_CHANGE(256, 0, MemoryTracker::kUnmanaged);
@@ -515,14 +533,14 @@
 }
 
 TEST_F(TextureMemoryTrackerTest, SetParameterPool) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, 128, MemoryTracker::kUnmanaged);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   EXPECT_MEMORY_ALLOCATION_CHANGE(128, 0, MemoryTracker::kUnmanaged);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, 128, MemoryTracker::kManaged);
   SetParameter(
-      texture_, GL_TEXTURE_POOL_CHROMIUM, GL_TEXTURE_POOL_MANAGED_CHROMIUM,
+      texture_ref_, GL_TEXTURE_POOL_CHROMIUM, GL_TEXTURE_POOL_MANAGED_CHROMIUM,
       GL_NO_ERROR);
   // Add expectation for texture deletion.
   EXPECT_MEMORY_ALLOCATION_CHANGE(128, 0, MemoryTracker::kManaged);
@@ -531,116 +549,119 @@
 }
 
 TEST_F(TextureTest, POT2D) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture_->target());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
   // Check Setting level 0 to POT
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
+  EXPECT_EQ(0, texture->num_uncleared_mips());
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
   // Set filters to something that will work with a single mip.
-  SetParameter(texture_, GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR);
-  EXPECT_TRUE(manager_->CanRender(texture_));
+  SetParameter(texture_ref_, GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR);
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   // Set them back.
-  SetParameter(
-      texture_, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_MIN_FILTER,
+               GL_LINEAR_MIPMAP_LINEAR, GL_NO_ERROR);
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
 
-  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_));
+  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_));
   // Make mips.
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(manager_->CanRender(texture_));
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   // Change a mip.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
   // Set a level past the number of mips that would get generated.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_));
+  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_));
   // Make mips.
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
-  EXPECT_TRUE(manager_->CanRender(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
 }
 
 TEST_F(TextureMemoryTrackerTest, MarkMipmapsGenerated) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, 64, MemoryTracker::kUnmanaged);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   EXPECT_MEMORY_ALLOCATION_CHANGE(64, 0, MemoryTracker::kUnmanaged);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, 84, MemoryTracker::kUnmanaged);
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
   EXPECT_MEMORY_ALLOCATION_CHANGE(84, 0, MemoryTracker::kUnmanaged);
   EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0, MemoryTracker::kUnmanaged);
 }
 
 TEST_F(TextureTest, UnusedMips) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture_->target());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
   // Set level zero to large size.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(manager_->CanRender(texture_));
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   // Set level zero to large smaller (levels unused mips)
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(manager_->CanRender(texture_));
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   // Set an unused level to some size
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 4, GL_RGBA, 16, 16, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(manager_->CanRender(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
 }
 
 TEST_F(TextureTest, NPOT2D) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture_->target());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
   // Check Setting level 0 to NPOT
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_TRUE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  EXPECT_TRUE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  SetParameter(texture_, GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR);
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  SetParameter(texture_ref_, GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR);
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  SetParameter(texture_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  SetParameter(texture_ref_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  SetParameter(texture_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
-  EXPECT_TRUE(manager_->CanRender(texture_));
+  SetParameter(texture_ref_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   // Change it to POT.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
 }
 
@@ -652,190 +673,195 @@
   TextureManager manager(
       NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.CreateTexture(kClient1Id, kService1Id);
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
 
-  manager.SetTarget(texture, GL_TEXTURE_2D);
+  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
   EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
   // Check Setting level 0 to NPOT
-  manager.SetLevelInfo(texture,
+  manager.SetLevelInfo(texture_ref,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   EXPECT_TRUE(TextureTestHelper::IsNPOT(texture));
   EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
-  EXPECT_TRUE(manager.CanGenerateMipmaps(texture));
-  EXPECT_FALSE(manager.CanRender(texture));
+  EXPECT_TRUE(manager.CanGenerateMipmaps(texture_ref));
+  EXPECT_FALSE(manager.CanRender(texture_ref));
   EXPECT_TRUE(manager.HaveUnrenderableTextures());
-  EXPECT_TRUE(manager.MarkMipmapsGenerated(texture));
+  EXPECT_TRUE(manager.MarkMipmapsGenerated(texture_ref));
   EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
-  EXPECT_TRUE(manager.CanRender(texture));
+  EXPECT_TRUE(manager.CanRender(texture_ref));
   EXPECT_FALSE(manager.HaveUnrenderableTextures());
   manager.Destroy(false);
 }
 
 TEST_F(TextureTest, POTCubeMap) {
-  manager_->SetTarget(texture_, GL_TEXTURE_CUBE_MAP);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture_->target());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_CUBE_MAP);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture->target());
   // Check Setting level 0 each face to POT
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_POSITIVE_X,
       0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
       0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
       0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
       0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
       0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
       0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_));
-  EXPECT_FALSE(manager_->CanRender(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_));
+  EXPECT_FALSE(manager_->CanRender(texture_ref_));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
 
   // Make mips.
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_TRUE(manager_->CanRender(texture_));
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_TRUE(manager_->CanRender(texture_ref_));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
 
   // Change a mip.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
       1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture_));
-  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture_));
-  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_));
+  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
+  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
+  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_));
   // Set a level past the number of mips that would get generated.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
       3, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_));
+  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_));
   // Make mips.
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture_));
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
 }
 
 TEST_F(TextureTest, GetLevelSize) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   GLsizei width = -1;
   GLsizei height = -1;
-  EXPECT_FALSE(texture_->GetLevelSize(GL_TEXTURE_2D, -1, &width, &height));
-  EXPECT_FALSE(texture_->GetLevelSize(GL_TEXTURE_2D, 1000, &width, &height));
-  EXPECT_FALSE(texture_->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height));
-  EXPECT_TRUE(texture_->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height));
+  Texture* texture = texture_ref_->texture();
+  EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, -1, &width, &height));
+  EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 1000, &width, &height));
+  EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height));
+  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height));
   EXPECT_EQ(4, width);
   EXPECT_EQ(5, height);
   manager_->RemoveTexture(kClient1Id);
-  EXPECT_TRUE(texture_->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height));
+  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D, 1, &width, &height));
   EXPECT_EQ(4, width);
   EXPECT_EQ(5, height);
 }
 
 TEST_F(TextureTest, GetLevelType) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   GLenum type = -1;
   GLenum format = -1;
-  EXPECT_FALSE(texture_->GetLevelType(GL_TEXTURE_2D, -1, &type, &format));
-  EXPECT_FALSE(texture_->GetLevelType(GL_TEXTURE_2D, 1000, &type, &format));
-  EXPECT_FALSE(texture_->GetLevelType(GL_TEXTURE_2D, 0, &type, &format));
-  EXPECT_TRUE(texture_->GetLevelType(GL_TEXTURE_2D, 1, &type, &format));
+  Texture* texture = texture_ref_->texture();
+  EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, -1, &type, &format));
+  EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, 1000, &type, &format));
+  EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, 0, &type, &format));
+  EXPECT_TRUE(texture->GetLevelType(GL_TEXTURE_2D, 1, &type, &format));
   EXPECT_EQ(static_cast<GLenum>(GL_UNSIGNED_BYTE), type);
   EXPECT_EQ(static_cast<GLenum>(GL_RGBA), format);
   manager_->RemoveTexture(kClient1Id);
-  EXPECT_TRUE(texture_->GetLevelType(GL_TEXTURE_2D, 1, &type, &format));
+  EXPECT_TRUE(texture->GetLevelType(GL_TEXTURE_2D, 1, &type, &format));
   EXPECT_EQ(static_cast<GLenum>(GL_UNSIGNED_BYTE), type);
   EXPECT_EQ(static_cast<GLenum>(GL_RGBA), format);
 }
 
 TEST_F(TextureTest, ValidForTexture) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   // Check bad face.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  Texture* texture = texture_ref_->texture();
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
       1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad level.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 0, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad xoffset.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, -1, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad xoffset + width > width.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 1, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad yoffset.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, -1, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad yoffset + height > height.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, 1, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad width.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, 0, 5, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad height.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, 0, 4, 6, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check bad format.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGB, GL_UNSIGNED_BYTE));
   // Check bad type.
-  EXPECT_FALSE(texture_->ValidForTexture(
+  EXPECT_FALSE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4));
   // Check valid full size
-  EXPECT_TRUE(texture_->ValidForTexture(
+  EXPECT_TRUE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
   // Check valid particial size.
-  EXPECT_TRUE(texture_->ValidForTexture(
+  EXPECT_TRUE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 1, 1, 2, 3, GL_RGBA, GL_UNSIGNED_BYTE));
   manager_->RemoveTexture(kClient1Id);
-  EXPECT_TRUE(texture_->ValidForTexture(
+  EXPECT_TRUE(texture->ValidForTexture(
       GL_TEXTURE_2D, 1, 0, 0, 4, 5, GL_RGBA, GL_UNSIGNED_BYTE));
 }
 
@@ -847,20 +873,21 @@
   TextureManager manager(
       NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.CreateTexture(kClient1Id, kService1Id);
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
-  manager.SetTarget(texture, GL_TEXTURE_2D);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
-  manager.SetLevelInfo(texture,
+  manager.SetLevelInfo(texture_ref,
       GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT, true);
   EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
   TestHelper::SetTexParameterWithExpectations(
       gl_.get(), error_state_.get(), &manager,
-      texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
+      texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
   EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
   TestHelper::SetTexParameterWithExpectations(
-      gl_.get(), error_state_.get(), &manager,
-      texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR);
+      gl_.get(), error_state_.get(), &manager, texture_ref,
+      GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR);
   EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
   manager.Destroy(false);
 }
@@ -873,11 +900,12 @@
   TextureManager manager(
       NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.CreateTexture(kClient1Id, kService1Id);
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
-  manager.SetTarget(texture, GL_TEXTURE_2D);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
-  manager.SetLevelInfo(texture,
+  manager.SetLevelInfo(texture_ref,
       GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT, true);
   EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
   manager.Destroy(false);
@@ -891,20 +919,21 @@
   TextureManager manager(
       NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.CreateTexture(kClient1Id, kService1Id);
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
-  manager.SetTarget(texture, GL_TEXTURE_2D);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
-  manager.SetLevelInfo(texture,
+  manager.SetLevelInfo(texture_ref,
       GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES, true);
   EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
   TestHelper::SetTexParameterWithExpectations(
       gl_.get(), error_state_.get(), &manager,
-      texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
+      texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
   EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
   TestHelper::SetTexParameterWithExpectations(
-      gl_.get(), error_state_.get(), &manager,
-      texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR);
+      gl_.get(), error_state_.get(), &manager, texture_ref,
+      GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR);
   EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
   manager.Destroy(false);
 }
@@ -917,11 +946,12 @@
   TextureManager manager(
       NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.CreateTexture(kClient1Id, kService1Id);
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
-  manager.SetTarget(texture, GL_TEXTURE_2D);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
-  manager.SetLevelInfo(texture,
+  manager.SetLevelInfo(texture_ref,
       GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES, true);
   EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
   manager.Destroy(false);
@@ -935,11 +965,12 @@
   TextureManager manager(
       NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.CreateTexture(kClient1Id, kService1Id);
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
-  manager.SetTarget(texture, GL_TEXTURE_EXTERNAL_OES);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  manager.SetTarget(texture_ref, GL_TEXTURE_EXTERNAL_OES);
+  Texture* texture = texture_ref->texture();
   EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES), texture->target());
-  EXPECT_FALSE(manager.CanGenerateMipmaps(texture));
+  EXPECT_FALSE(manager.CanGenerateMipmaps(texture_ref));
   manager.Destroy(false);
 }
 
@@ -951,13 +982,13 @@
   TextureManager manager(
       NULL, feature_info.get(), kMaxTextureSize, kMaxCubeMapTextureSize);
   manager.CreateTexture(kClient1Id, kService1Id);
-  Texture* texture = manager.GetTexture(kClient1Id);
-  ASSERT_TRUE(texture != NULL);
-  manager.SetTarget(texture, GL_TEXTURE_2D);
+  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
+  ASSERT_TRUE(texture_ref != NULL);
+  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
   manager.SetLevelInfo(
-      texture, GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 4, 4, 1, 0,
+      texture_ref, GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 4, 4, 1, 0,
       GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, false);
-  EXPECT_FALSE(manager.CanGenerateMipmaps(texture));
+  EXPECT_FALSE(manager.CanGenerateMipmaps(texture_ref));
   manager.Destroy(false);
 }
 
@@ -967,101 +998,104 @@
   static const GLuint kClient3Id = 3;
   static const GLuint kService3Id = 13;
   EXPECT_FALSE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(0, texture->num_uncleared_mips());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(1, texture_->num_uncleared_mips());
-  manager_->SetLevelCleared(texture_, GL_TEXTURE_2D, 0, true);
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(1, texture->num_uncleared_mips());
+  manager_->SetLevelCleared(texture_ref_, GL_TEXTURE_2D, 0, true);
+  EXPECT_TRUE(texture->SafeToRenderFrom());
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
-  manager_->SetLevelInfo(texture_,
+  EXPECT_EQ(0, texture->num_uncleared_mips());
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(1, texture_->num_uncleared_mips());
-  manager_->SetLevelCleared(texture_, GL_TEXTURE_2D, 1, true);
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(1, texture->num_uncleared_mips());
+  manager_->SetLevelCleared(texture_ref_, GL_TEXTURE_2D, 1, true);
+  EXPECT_TRUE(texture->SafeToRenderFrom());
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
-  manager_->SetLevelInfo(texture_,
+  EXPECT_EQ(0, texture->num_uncleared_mips());
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(2, texture_->num_uncleared_mips());
-  manager_->SetLevelCleared(texture_, GL_TEXTURE_2D, 0, true);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(2, texture->num_uncleared_mips());
+  manager_->SetLevelCleared(texture_ref_, GL_TEXTURE_2D, 0, true);
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(1, texture_->num_uncleared_mips());
-  manager_->SetLevelCleared(texture_, GL_TEXTURE_2D, 1, true);
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(1, texture->num_uncleared_mips());
+  manager_->SetLevelCleared(texture_ref_, GL_TEXTURE_2D, 1, true);
+  EXPECT_TRUE(texture->SafeToRenderFrom());
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
-  manager_->SetLevelInfo(texture_,
+  EXPECT_EQ(0, texture->num_uncleared_mips());
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(1, texture_->num_uncleared_mips());
-  manager_->MarkMipmapsGenerated(texture_);
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(1, texture->num_uncleared_mips());
+  manager_->MarkMipmapsGenerated(texture_ref_);
+  EXPECT_TRUE(texture->SafeToRenderFrom());
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
+  EXPECT_EQ(0, texture->num_uncleared_mips());
 
   manager_->CreateTexture(kClient2Id, kService2Id);
-  scoped_refptr<Texture> texture2(
+  scoped_refptr<TextureRef> texture_ref2(
       manager_->GetTexture(kClient2Id));
-  ASSERT_TRUE(texture2.get() != NULL);
-  manager_->SetTarget(texture2, GL_TEXTURE_2D);
+  ASSERT_TRUE(texture_ref2.get() != NULL);
+  manager_->SetTarget(texture_ref2, GL_TEXTURE_2D);
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
+  Texture* texture2 = texture_ref2->texture();
   EXPECT_EQ(0, texture2->num_uncleared_mips());
-  manager_->SetLevelInfo(texture2,
+  manager_->SetLevelInfo(texture_ref2,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
   EXPECT_EQ(0, texture2->num_uncleared_mips());
-  manager_->SetLevelInfo(texture2,
+  manager_->SetLevelInfo(texture_ref2,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
   EXPECT_EQ(1, texture2->num_uncleared_mips());
 
   manager_->CreateTexture(kClient3Id, kService3Id);
-  scoped_refptr<Texture> texture3(
+  scoped_refptr<TextureRef> texture_ref3(
       manager_->GetTexture(kClient3Id));
-  ASSERT_TRUE(texture3.get() != NULL);
-  manager_->SetTarget(texture3, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture3,
+  ASSERT_TRUE(texture_ref3.get() != NULL);
+  manager_->SetTarget(texture_ref3, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref3,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
+  Texture* texture3 = texture_ref3->texture();
   EXPECT_EQ(1, texture3->num_uncleared_mips());
-  manager_->SetLevelCleared(texture2, GL_TEXTURE_2D, 0, true);
+  manager_->SetLevelCleared(texture_ref2, GL_TEXTURE_2D, 0, true);
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
   EXPECT_EQ(0, texture2->num_uncleared_mips());
-  manager_->SetLevelCleared(texture3, GL_TEXTURE_2D, 0, true);
+  manager_->SetLevelCleared(texture_ref3, GL_TEXTURE_2D, 0, true);
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
   EXPECT_EQ(0, texture3->num_uncleared_mips());
 
-  manager_->SetLevelInfo(texture2,
+  manager_->SetLevelInfo(texture_ref2,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  manager_->SetLevelInfo(texture3,
+  manager_->SetLevelInfo(texture_ref3,
       GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
@@ -1076,13 +1110,13 @@
   EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService2Id)))
       .Times(1)
       .RetiresOnSaturation();
-  texture2 = NULL;
+  texture_ref2 = NULL;
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
   EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService3Id)))
       .Times(1)
       .RetiresOnSaturation();
-  texture3 = NULL;
+  texture_ref3 = NULL;
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
 }
@@ -1090,88 +1124,90 @@
 TEST_F(TextureTest, ClearTexture) {
   EXPECT_CALL(*decoder_, ClearLevel(_, _, _, _, _, _, _, _, _))
       .WillRepeatedly(Return(true));
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  Texture* texture = texture_ref_->texture();
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(2, texture_->num_uncleared_mips());
-  manager_->ClearRenderableLevels(decoder_.get(), texture_);
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(2, texture->num_uncleared_mips());
+  manager_->ClearRenderableLevels(decoder_.get(), texture_ref_);
+  EXPECT_TRUE(texture->SafeToRenderFrom());
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
-  manager_->SetLevelInfo(texture_,
+  EXPECT_EQ(0, texture->num_uncleared_mips());
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(2, texture_->num_uncleared_mips());
-  manager_->ClearTextureLevel(decoder_.get(), texture_, GL_TEXTURE_2D, 0);
-  EXPECT_FALSE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(2, texture->num_uncleared_mips());
+  manager_->ClearTextureLevel(decoder_.get(), texture_ref_, GL_TEXTURE_2D, 0);
+  EXPECT_FALSE(texture->SafeToRenderFrom());
   EXPECT_TRUE(manager_->HaveUnsafeTextures());
   EXPECT_TRUE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(1, texture_->num_uncleared_mips());
-  manager_->ClearTextureLevel(decoder_.get(), texture_, GL_TEXTURE_2D, 1);
-  EXPECT_TRUE(texture_->SafeToRenderFrom());
+  EXPECT_EQ(1, texture->num_uncleared_mips());
+  manager_->ClearTextureLevel(decoder_.get(), texture_ref_, GL_TEXTURE_2D, 1);
+  EXPECT_TRUE(texture->SafeToRenderFrom());
   EXPECT_FALSE(manager_->HaveUnsafeTextures());
   EXPECT_FALSE(manager_->HaveUnclearedMips());
-  EXPECT_EQ(0, texture_->num_uncleared_mips());
+  EXPECT_EQ(0, texture->num_uncleared_mips());
 }
 
 TEST_F(TextureTest, UseDeletedTexture) {
   static const GLuint kClient2Id = 2;
   static const GLuint kService2Id = 12;
   // Make the default texture renderable
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   // Make a new texture
   manager_->CreateTexture(kClient2Id, kService2Id);
-  scoped_refptr<Texture> texture(
+  scoped_refptr<TextureRef> texture_ref(
       manager_->GetTexture(kClient2Id));
-  manager_->SetTarget(texture, GL_TEXTURE_2D);
-  EXPECT_FALSE(manager_->CanRender(texture));
+  manager_->SetTarget(texture_ref, GL_TEXTURE_2D);
+  EXPECT_FALSE(manager_->CanRender(texture_ref));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
   // Remove it.
   manager_->RemoveTexture(kClient2Id);
-  EXPECT_FALSE(manager_->CanRender(texture));
+  EXPECT_FALSE(manager_->CanRender(texture_ref));
   EXPECT_TRUE(manager_->HaveUnrenderableTextures());
   // Check that we can still manipulate it and it effects the manager.
-  manager_->SetLevelInfo(texture,
+  manager_->SetLevelInfo(texture_ref,
       GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
-  EXPECT_TRUE(manager_->CanRender(texture));
+  EXPECT_TRUE(manager_->CanRender(texture_ref));
   EXPECT_FALSE(manager_->HaveUnrenderableTextures());
   EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService2Id)))
       .Times(1)
       .RetiresOnSaturation();
-  texture = NULL;
+  texture_ref = NULL;
 }
 
 TEST_F(TextureTest, GetLevelImage) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_TRUE(texture_->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
   // Set image.
-  manager_->SetLevelImage(texture_,
+  manager_->SetLevelImage(texture_ref_,
       GL_TEXTURE_2D, 1, gfx::GLImage::CreateGLImage(0));
-  EXPECT_FALSE(texture_->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
+  EXPECT_FALSE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
   // Remove it.
-  manager_->SetLevelImage(texture_, GL_TEXTURE_2D, 1, NULL);
-  EXPECT_TRUE(texture_->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
-  manager_->SetLevelImage(texture_,
+  manager_->SetLevelImage(texture_ref_, GL_TEXTURE_2D, 1, NULL);
+  EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
+  manager_->SetLevelImage(texture_ref_,
       GL_TEXTURE_2D, 1, gfx::GLImage::CreateGLImage(0));
   // Image should be reset when SetLevelInfo is called.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  EXPECT_TRUE(texture_->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
+  EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
 }
 
 namespace {
@@ -1185,105 +1221,105 @@
 }  // anonymous namespace
 
 TEST_F(TextureTest, AddToSignature) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  manager_->SetLevelInfo(texture_,
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   std::string signature1;
   std::string signature2;
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature1);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature1);
 
   std::set<std::string> string_set;
   EXPECT_FALSE(InSet(&string_set, signature1));
 
   // check changing 1 thing makes a different signature.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 4, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
   // check putting it back makes the same signature.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_EQ(signature1, signature2);
 
   // Check setting cleared status does not change signature.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_EQ(signature1, signature2);
 
   // Check changing other settings changes signature.
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, false);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, false);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, false);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_FLOAT,
       false);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
   // put it back
-  manager_->SetLevelInfo(texture_,
+  manager_->SetLevelInfo(texture_ref_,
       GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
       false);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_EQ(signature1, signature2);
 
   // check changing parameters changes signature.
-  SetParameter(texture_, GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  SetParameter(
-      texture_, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR, GL_NO_ERROR);
-  SetParameter(texture_, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_MIN_FILTER,
+               GL_NEAREST_MIPMAP_LINEAR, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  SetParameter(texture_, GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_NO_ERROR);
-  SetParameter(texture_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
-  SetParameter(texture_, GL_TEXTURE_WRAP_S, GL_REPEAT, GL_NO_ERROR);
-  SetParameter(texture_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_WRAP_S, GL_REPEAT, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_FALSE(InSet(&string_set, signature2));
 
   // Check putting it back genenerates the same signature
-  SetParameter(texture_, GL_TEXTURE_WRAP_T, GL_REPEAT, GL_NO_ERROR);
+  SetParameter(texture_ref_, GL_TEXTURE_WRAP_T, GL_REPEAT, GL_NO_ERROR);
   signature2.clear();
-  manager_->AddToSignature(texture_, GL_TEXTURE_2D, 1, &signature2);
+  manager_->AddToSignature(texture_ref_, GL_TEXTURE_2D, 1, &signature2);
   EXPECT_EQ(signature1, signature2);
 
   // Check the set was acutally getting different signatures.
@@ -1364,8 +1400,10 @@
     bool cleared;
   };
 
-  void SetLevelInfo(Texture* texture, GLint level, const LevelInfo& info) {
-    manager_->SetLevelInfo(texture,
+  void SetLevelInfo(TextureRef* texture_ref,
+                    GLint level,
+                    const LevelInfo& info) {
+    manager_->SetLevelInfo(texture_ref,
                            info.target,
                            level,
                            info.format,
@@ -1378,9 +1416,10 @@
                            info.cleared);
   }
 
-  static LevelInfo GetLevelInfo(const Texture* texture,
+  static LevelInfo GetLevelInfo(const TextureRef* texture_ref,
                                 GLint target,
                                 GLint level) {
+    const Texture* texture = texture_ref->texture();
     LevelInfo info;
     info.target = target;
     EXPECT_TRUE(texture->GetLevelSize(target, level, &info.width,
@@ -1391,27 +1430,27 @@
     return info;
   }
 
-  TextureDefinition* Save(Texture* texture) {
+  TextureDefinition* Save(TextureRef* texture_ref) {
     EXPECT_CALL(*gl_, GenTextures(_, _))
         .WillOnce(SetArgumentPointee<1>(kEmptyTextureServiceId));
-    TextureDefinition* definition = manager_->Save(texture);
+    TextureDefinition* definition = manager_->Save(texture_ref);
     EXPECT_TRUE(definition != NULL);
     return definition;
   }
 
-  void Restore(Texture* texture, TextureDefinition* definition) {
-    EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(texture->service_id())))
+  void Restore(TextureRef* texture_ref, TextureDefinition* definition) {
+    EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(texture_ref->service_id())))
         .Times(1).RetiresOnSaturation();
     EXPECT_CALL(*gl_,
                 BindTexture(definition->target(), definition->service_id()))
         .Times(1).RetiresOnSaturation();
     EXPECT_CALL(*gl_, TexParameteri(_, _, _)).Times(AtLeast(1));
 
-    EXPECT_TRUE(
-        manager_->Restore("TextureTest", decoder_.get(), texture, definition));
+    EXPECT_TRUE(manager_->Restore("TextureTest", decoder_.get(),
+                                  texture_ref, definition));
   }
 
-  scoped_refptr<Texture> texture2_;
+  scoped_refptr<TextureRef> texture2_;
 
  private:
   static const GLuint kEmptyTextureServiceId;
@@ -1424,16 +1463,17 @@
 const GLuint SaveRestoreTextureTest::kEmptyTextureServiceId = 13;
 
 TEST_F(SaveRestoreTextureTest, SaveRestore2D) {
-  manager_->SetTarget(texture_, GL_TEXTURE_2D);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture_->target());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_2D);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
   LevelInfo level0(
       GL_TEXTURE_2D, GL_RGBA, 4, 4, 1, 0, GL_UNSIGNED_BYTE, true);
-  SetLevelInfo(texture_, 0, level0);
-  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  LevelInfo level1 = GetLevelInfo(texture_.get(), GL_TEXTURE_2D, 1);
-  LevelInfo level2 = GetLevelInfo(texture_.get(), GL_TEXTURE_2D, 2);
-  scoped_ptr<TextureDefinition> definition(Save(texture_));
+  SetLevelInfo(texture_ref_, 0, level0);
+  EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture_ref_));
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  LevelInfo level1 = GetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1);
+  LevelInfo level2 = GetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 2);
+  scoped_ptr<TextureDefinition> definition(Save(texture_ref_));
   const TextureDefinition::LevelInfos& infos = definition->level_infos();
   EXPECT_EQ(1U, infos.size());
   EXPECT_EQ(3U, infos[0].size());
@@ -1446,27 +1486,29 @@
       0,
       LevelInfo(GL_TEXTURE_2D, GL_RGBA, 16, 16, 1, 0, GL_UNSIGNED_BYTE, false));
   EXPECT_TRUE(manager_->MarkMipmapsGenerated(texture2_));
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture2_));
-  EXPECT_EQ(1024U + 256U + 64U + 16U + 4U, texture2_->estimated_size());
+  texture = texture2_->texture();
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  EXPECT_EQ(1024U + 256U + 64U + 16U + 4U, texture->estimated_size());
   Restore(texture2_, definition.release());
   EXPECT_EQ(level0, GetLevelInfo(texture2_.get(), GL_TEXTURE_2D, 0));
   EXPECT_EQ(level1, GetLevelInfo(texture2_.get(), GL_TEXTURE_2D, 1));
   EXPECT_EQ(level2, GetLevelInfo(texture2_.get(), GL_TEXTURE_2D, 2));
-  EXPECT_EQ(64U + 16U + 4U, texture2_->estimated_size());
+  EXPECT_EQ(64U + 16U + 4U, texture->estimated_size());
   GLint w, h;
-  EXPECT_TRUE(texture2_->GetLevelSize(GL_TEXTURE_2D, 3, &w, &h));
+  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D, 3, &w, &h));
   EXPECT_EQ(0, w);
   EXPECT_EQ(0, h);
 }
 
 TEST_F(SaveRestoreTextureTest, SaveRestoreClearRectangle) {
-  manager_->SetTarget(texture_, GL_TEXTURE_RECTANGLE_ARB);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB), texture_->target());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_RECTANGLE_ARB);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB), texture->target());
   LevelInfo level0(
       GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, 1, 1, 1, 0, GL_UNSIGNED_BYTE, false);
-  SetLevelInfo(texture_, 0, level0);
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  scoped_ptr<TextureDefinition> definition(Save(texture_));
+  SetLevelInfo(texture_ref_, 0, level0);
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  scoped_ptr<TextureDefinition> definition(Save(texture_ref_));
   const TextureDefinition::LevelInfos& infos = definition->level_infos();
   EXPECT_EQ(1U, infos.size());
   EXPECT_EQ(1U, infos[0].size());
@@ -1483,22 +1525,24 @@
 }
 
 TEST_F(SaveRestoreTextureTest, SaveRestoreStreamTexture) {
-  manager_->SetTarget(texture_, GL_TEXTURE_EXTERNAL_OES);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES), texture_->target());
-  texture_->SetStreamTexture(true);
-  GLuint service_id = texture_->service_id();
-  scoped_ptr<TextureDefinition> definition(Save(texture_));
-  EXPECT_FALSE(texture_->IsStreamTexture());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_EXTERNAL_OES);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES), texture->target());
+  manager_->SetStreamTexture(texture_ref_, true);
+  GLuint service_id = texture->service_id();
+  scoped_ptr<TextureDefinition> definition(Save(texture_ref_));
+  EXPECT_FALSE(texture->IsStreamTexture());
   manager_->SetTarget(texture2_, GL_TEXTURE_EXTERNAL_OES);
   Restore(texture2_, definition.release());
-  EXPECT_TRUE(texture2_->IsStreamTexture());
-  EXPECT_TRUE(texture2_->IsImmutable());
+  EXPECT_TRUE(texture2_->texture()->IsStreamTexture());
+  EXPECT_TRUE(texture2_->texture()->IsImmutable());
   EXPECT_EQ(service_id, texture2_->service_id());
 }
 
 TEST_F(SaveRestoreTextureTest, SaveRestoreCube) {
-  manager_->SetTarget(texture_, GL_TEXTURE_CUBE_MAP);
-  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture_->target());
+  manager_->SetTarget(texture_ref_, GL_TEXTURE_CUBE_MAP);
+  Texture* texture = texture_ref_->texture();
+  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture->target());
   LevelInfo face0(GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                   GL_RGBA,
                   1,
@@ -1515,10 +1559,10 @@
                   0,
                   GL_UNSIGNED_BYTE,
                   true);
-  SetLevelInfo(texture_, 0, face0);
-  SetLevelInfo(texture_, 0, face5);
-  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture_));
-  scoped_ptr<TextureDefinition> definition(Save(texture_));
+  SetLevelInfo(texture_ref_, 0, face0);
+  SetLevelInfo(texture_ref_, 0, face5);
+  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
+  scoped_ptr<TextureDefinition> definition(Save(texture_ref_));
   const TextureDefinition::LevelInfos& infos = definition->level_infos();
   EXPECT_EQ(6U, infos.size());
   EXPECT_EQ(1U, infos[0].size());
@@ -1530,7 +1574,250 @@
             GetLevelInfo(texture2_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0));
 }
 
+class CountingMemoryTracker : public MemoryTracker {
+ public:
+  CountingMemoryTracker() {
+    current_size_[0] = 0;
+    current_size_[1] = 0;
+  }
+
+  virtual void TrackMemoryAllocatedChange(size_t old_size,
+                                          size_t new_size,
+                                          Pool pool)  OVERRIDE {
+    DCHECK_LT(static_cast<size_t>(pool), arraysize(current_size_));
+    current_size_[pool] += new_size - old_size;
+  }
+
+  virtual bool EnsureGPUMemoryAvailable(size_t size_needed) OVERRIDE {
+    return true;
+  }
+
+  size_t GetSize(Pool pool) {
+    DCHECK_LT(static_cast<size_t>(pool), arraysize(current_size_));
+    return current_size_[pool];
+  }
+
+ private:
+  virtual ~CountingMemoryTracker() {}
+
+  size_t current_size_[2];
+  DISALLOW_COPY_AND_ASSIGN(CountingMemoryTracker);
+};
+
+class SharedTextureTest : public testing::Test {
+ public:
+  SharedTextureTest()
+      : feature_info_(new FeatureInfo()) {
+  }
+
+  virtual ~SharedTextureTest() {
+  }
+
+  virtual void SetUp() {
+    gl_.reset(new ::gfx::MockGLInterface());
+    ::gfx::GLInterface::SetGLInterface(gl_.get());
+
+    memory_tracker1_ = new CountingMemoryTracker;
+    texture_manager1_.reset(new TextureManager(
+        memory_tracker1_, feature_info_.get(),
+        TextureManagerTest::kMaxTextureSize,
+        TextureManagerTest::kMaxCubeMapTextureSize));
+    memory_tracker2_ = new CountingMemoryTracker;
+    texture_manager2_.reset(new TextureManager(
+        memory_tracker2_, feature_info_.get(),
+        TextureManagerTest::kMaxTextureSize,
+        TextureManagerTest::kMaxCubeMapTextureSize));
+    TestHelper::SetupTextureManagerInitExpectations(gl_.get(), "");
+    texture_manager1_->Initialize();
+    TestHelper::SetupTextureManagerInitExpectations(gl_.get(), "");
+    texture_manager2_->Initialize();
+  }
+
+  virtual void TearDown() {
+    texture_manager2_->Destroy(false);
+    texture_manager2_.reset();
+    texture_manager1_->Destroy(false);
+    texture_manager1_.reset();
+    ::gfx::GLInterface::SetGLInterface(NULL);
+    gl_.reset();
+  }
+
+ protected:
+  scoped_ptr< ::gfx::MockGLInterface > gl_;
+  scoped_refptr<FeatureInfo> feature_info_;
+  scoped_refptr<CountingMemoryTracker> memory_tracker1_;
+  scoped_ptr<TextureManager> texture_manager1_;
+  scoped_refptr<CountingMemoryTracker> memory_tracker2_;
+  scoped_ptr<TextureManager> texture_manager2_;
+};
+
+TEST_F(SharedTextureTest, DeleteTextures) {
+  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
+  scoped_refptr<TextureRef> ref2 = new TextureRef(texture_manager2_.get(),
+                                                  ref1->texture());
+  EXPECT_CALL(*gl_, DeleteTextures(1, _))
+      .Times(0);
+  ref1 = NULL;
+  texture_manager1_->RemoveTexture(10);
+  testing::Mock::VerifyAndClearExpectations(gl_.get());
+
+  EXPECT_CALL(*gl_, DeleteTextures(1, _))
+      .Times(1)
+      .RetiresOnSaturation();
+  ref2 = NULL;
+  testing::Mock::VerifyAndClearExpectations(gl_.get());
+}
+
+TEST_F(SharedTextureTest, TextureSafetyAccounting) {
+  EXPECT_FALSE(texture_manager1_->HaveUnrenderableTextures());
+  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());
+  EXPECT_FALSE(texture_manager2_->HaveUnrenderableTextures());
+  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());
+
+  // Newly created texture is unrenderable.
+  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
+  EXPECT_FALSE(texture_manager1_->HaveUnrenderableTextures());
+  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());
+
+  // Associate new texture ref to other texture manager, should account for it
+  // too.
+  scoped_refptr<TextureRef> ref2 = new TextureRef(texture_manager2_.get(),
+                                                  ref1->texture());
+  EXPECT_FALSE(texture_manager2_->HaveUnrenderableTextures());
+  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());
+
+  // Make texture renderable but uncleared on one texture manager, should affect
+  // other one.
+  texture_manager1_->SetTarget(ref1, GL_TEXTURE_2D);
+  EXPECT_TRUE(texture_manager1_->HaveUnrenderableTextures());
+  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());
+  EXPECT_TRUE(texture_manager2_->HaveUnrenderableTextures());
+  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());
+
+  texture_manager1_->SetLevelInfo(ref1, GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
+                                  GL_RGBA, GL_UNSIGNED_BYTE, false);
+  EXPECT_FALSE(texture_manager1_->HaveUnrenderableTextures());
+  EXPECT_TRUE(texture_manager1_->HaveUnsafeTextures());
+  EXPECT_TRUE(texture_manager1_->HaveUnclearedMips());
+  EXPECT_FALSE(texture_manager2_->HaveUnrenderableTextures());
+  EXPECT_TRUE(texture_manager2_->HaveUnsafeTextures());
+  EXPECT_TRUE(texture_manager2_->HaveUnclearedMips());
+
+  // Make texture cleared on one texture manager, should affect other one.
+  texture_manager1_->SetLevelCleared(ref1, GL_TEXTURE_2D, 0, true);
+  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());
+  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
+  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());
+
+  EXPECT_CALL(*gl_, DeleteTextures(1, _))
+      .Times(1)
+      .RetiresOnSaturation();
+  texture_manager1_->RemoveTexture(10);
+}
+
+TEST_F(SharedTextureTest, FBOCompletenessCheck) {
+  const GLenum kCompleteValue = GL_FRAMEBUFFER_COMPLETE;
+  FramebufferManager framebuffer_manager1(1, 1);
+  texture_manager1_->set_framebuffer_manager(&framebuffer_manager1);
+  FramebufferManager framebuffer_manager2(1, 1);
+  texture_manager2_->set_framebuffer_manager(&framebuffer_manager2);
+
+  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
+  framebuffer_manager1.CreateFramebuffer(10, 10);
+  scoped_refptr<Framebuffer> framebuffer1 =
+      framebuffer_manager1.GetFramebuffer(10);
+  framebuffer1->AttachTexture(GL_COLOR_ATTACHMENT0, ref1, GL_TEXTURE_2D, 0);
+  EXPECT_FALSE(framebuffer_manager1.IsComplete(framebuffer1));
+  EXPECT_NE(kCompleteValue, framebuffer1->IsPossiblyComplete());
+
+  // Make FBO complete in manager 1.
+  texture_manager1_->SetTarget(ref1, GL_TEXTURE_2D);
+  texture_manager1_->SetLevelInfo(ref1, GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
+                                  GL_RGBA, GL_UNSIGNED_BYTE, true);
+  EXPECT_EQ(kCompleteValue, framebuffer1->IsPossiblyComplete());
+  framebuffer_manager1.MarkAsComplete(framebuffer1);
+  EXPECT_TRUE(framebuffer_manager1.IsComplete(framebuffer1));
+
+  // Share texture with manager 2.
+  scoped_refptr<TextureRef> ref2 = new TextureRef(texture_manager2_.get(),
+                                                  ref1->texture());
+  framebuffer_manager2.CreateFramebuffer(20, 20);
+  scoped_refptr<Framebuffer> framebuffer2 =
+      framebuffer_manager2.GetFramebuffer(20);
+  framebuffer2->AttachTexture(GL_COLOR_ATTACHMENT0, ref2, GL_TEXTURE_2D, 0);
+  EXPECT_FALSE(framebuffer_manager2.IsComplete(framebuffer2));
+  EXPECT_EQ(kCompleteValue, framebuffer2->IsPossiblyComplete());
+  framebuffer_manager2.MarkAsComplete(framebuffer2);
+  EXPECT_TRUE(framebuffer_manager2.IsComplete(framebuffer2));
+
+  // Change level for texture, both FBOs should be marked incomplete
+  texture_manager1_->SetLevelInfo(ref1, GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
+                                  GL_RGBA, GL_UNSIGNED_BYTE, true);
+  EXPECT_FALSE(framebuffer_manager1.IsComplete(framebuffer1));
+  EXPECT_EQ(kCompleteValue, framebuffer1->IsPossiblyComplete());
+  framebuffer_manager1.MarkAsComplete(framebuffer1);
+  EXPECT_TRUE(framebuffer_manager1.IsComplete(framebuffer1));
+  EXPECT_FALSE(framebuffer_manager2.IsComplete(framebuffer2));
+  EXPECT_EQ(kCompleteValue, framebuffer2->IsPossiblyComplete());
+  framebuffer_manager2.MarkAsComplete(framebuffer2);
+  EXPECT_TRUE(framebuffer_manager2.IsComplete(framebuffer2));
+
+  EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, _))
+      .Times(2)
+      .RetiresOnSaturation();
+  framebuffer_manager1.RemoveFramebuffer(10);
+  framebuffer_manager2.RemoveFramebuffer(20);
+  EXPECT_CALL(*gl_, DeleteTextures(1, _))
+      .Times(1)
+      .RetiresOnSaturation();
+  texture_manager1_->RemoveTexture(10);
+}
+
+TEST_F(SharedTextureTest, Memory) {
+  size_t initial_memory1 = memory_tracker1_->GetSize(MemoryTracker::kUnmanaged);
+  size_t initial_memory2 = memory_tracker2_->GetSize(MemoryTracker::kUnmanaged);
+
+  // Newly created texture is unrenderable.
+  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
+  texture_manager1_->SetTarget(ref1, GL_TEXTURE_2D);
+  texture_manager1_->SetLevelInfo(ref1, GL_TEXTURE_2D, 0, GL_RGBA, 10, 10, 1, 0,
+                                  GL_RGBA, GL_UNSIGNED_BYTE, false);
+
+  EXPECT_LT(0u, ref1->texture()->estimated_size());
+  EXPECT_EQ(initial_memory1 + ref1->texture()->estimated_size(),
+            memory_tracker1_->GetSize(MemoryTracker::kUnmanaged));
+
+  // Associate new texture ref to other texture manager, it doesn't account for
+  // the texture memory, the first memory tracker still has it.
+  scoped_refptr<TextureRef> ref2 = new TextureRef(texture_manager2_.get(),
+                                                  ref1->texture());
+  EXPECT_EQ(initial_memory1 + ref1->texture()->estimated_size(),
+            memory_tracker1_->GetSize(MemoryTracker::kUnmanaged));
+  EXPECT_EQ(initial_memory2,
+            memory_tracker2_->GetSize(MemoryTracker::kUnmanaged));
+
+  // Delete the texture, memory should go to the remaining tracker.
+  texture_manager1_->RemoveTexture(10);
+  ref1 = NULL;
+  EXPECT_EQ(initial_memory1,
+            memory_tracker1_->GetSize(MemoryTracker::kUnmanaged));
+  EXPECT_EQ(initial_memory2 + ref2->texture()->estimated_size(),
+            memory_tracker2_->GetSize(MemoryTracker::kUnmanaged));
+
+  EXPECT_CALL(*gl_, DeleteTextures(1, _))
+      .Times(1)
+      .RetiresOnSaturation();
+  ref2 = NULL;
+  EXPECT_EQ(initial_memory2,
+            memory_tracker2_->GetSize(MemoryTracker::kUnmanaged));
+}
+
 }  // namespace gles2
 }  // namespace gpu
-
-
diff --git a/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittests.cc b/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittests.cc
new file mode 100644
index 0000000..ae2ec10
--- /dev/null
+++ b/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittests.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2013 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 <GLES2/gl2.h>
+#include <GLES2/gl2chromium.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_mock.h"
+#include "gpu/command_buffer/client/image_factory_mock.h"
+#include "gpu/command_buffer/service/image_manager.h"
+#include "gpu/command_buffer/tests/gl_manager.h"
+#include "gpu/command_buffer/tests/gl_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gl/gl_image.h"
+#include "ui/gl/gl_image_mock.h"
+
+using testing::_;
+using testing::IgnoreResult;
+using testing::InvokeWithoutArgs;
+using testing::Invoke;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+namespace gpu {
+namespace gles2 {
+
+static const int kImageWidth = 256;
+static const int kImageHeight = 256;
+static const int kImageBytesPerPixel = 4;
+
+class MockGpuMemoryBufferTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    GLManager::Options options;
+    image_manager_ = new ImageManager;
+    image_factory_.reset(
+        new StrictMock<ImageFactoryMock>(image_manager_));
+    options.image_manager = image_manager_;
+    options.image_factory = image_factory_.get();
+
+    gl_.Initialize(options);
+    gl_.MakeCurrent();
+  }
+
+  virtual void TearDown() {
+    gl_.Destroy();
+  }
+
+  scoped_ptr<StrictMock<ImageFactoryMock> > image_factory_;
+  scoped_refptr<ImageManager> image_manager_;
+  GLManager gl_;
+};
+
+// An end to end test that tests the whole GpuMemoryBuffer lifecycle.
+TEST_F(MockGpuMemoryBufferTest, Lifecycle) {
+  // Create a client texture id.
+  GLuint texture_id;
+  glGenTextures(1, &texture_id);
+
+  // Buffer is owned and freed by GpuMemoryBufferTracker.
+  StrictMock<GpuMemoryBufferMock>* gpu_memory_buffer =
+      new StrictMock<GpuMemoryBufferMock>(kImageWidth, kImageHeight);
+
+  const GLuint kImageId = 345u;
+
+  EXPECT_CALL(*image_factory_.get(), CreateGpuMemoryBufferMock(
+      kImageWidth, kImageHeight, GL_RGBA8_OES, _))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<3>(kImageId),
+                      Return(gpu_memory_buffer)))
+      .RetiresOnSaturation();
+
+  // Create the GLImage and insert it into the ImageManager, which
+  // would be done within CreateGpuMemoryBufferMock if it weren't a mock.
+  GLuint image_id = glCreateImageCHROMIUM(
+      kImageWidth, kImageHeight, GL_RGBA8_OES);
+  EXPECT_EQ(kImageId, image_id);
+
+  gfx::Size size(kImageWidth, kImageHeight);
+  scoped_refptr<gfx::GLImageMock> gl_image(
+      new gfx::GLImageMock(gpu_memory_buffer, size));
+  image_manager_->AddImage(gl_image.get(), image_id);
+
+  EXPECT_CALL(*gpu_memory_buffer, IsMapped())
+      .WillOnce(Return(false))
+      .RetiresOnSaturation();
+
+  scoped_ptr<uint8[]> buffer_pixels(new uint8[
+      kImageWidth * kImageHeight * kImageBytesPerPixel]);
+
+  EXPECT_CALL(*gpu_memory_buffer, Map(_, _))
+      .Times(1)
+      .WillOnce(SetArgPointee<1>(buffer_pixels.get()))
+      .RetiresOnSaturation();
+  void* mapped_buffer =
+      glMapImageCHROMIUM(image_id, GL_WRITE_ONLY);
+  EXPECT_EQ(buffer_pixels.get(), mapped_buffer);
+
+  EXPECT_CALL(*gpu_memory_buffer, IsMapped())
+      .WillOnce(Return(true))
+      .RetiresOnSaturation();
+
+  // Unmap the image.
+  EXPECT_CALL(*gpu_memory_buffer, Unmap())
+      .Times(1)
+      .RetiresOnSaturation();
+  glUnmapImageCHROMIUM(image_id);
+
+  // Bind the texture and the image.
+  glBindTexture(GL_TEXTURE_2D, texture_id);
+  EXPECT_CALL(*gl_image, BindTexImage())
+      .Times(1)
+      .WillOnce(Return(true))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_image, GetSize())
+      .Times(1)
+      .WillOnce(Return(size))
+      .RetiresOnSaturation();
+  glBindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id);
+
+  // Destroy the image.
+  EXPECT_CALL(*gpu_memory_buffer, Die())
+      .Times(1)
+      .RetiresOnSaturation();
+
+  EXPECT_CALL(*image_factory_.get(), DeleteGpuMemoryBuffer(image_id))
+      .Times(1)
+      .RetiresOnSaturation();
+
+  glDestroyImageCHROMIUM(image_id);
+
+  // Delete the texture.
+  glDeleteTextures(1, &texture_id);
+}
+
+}  // namespace gles2
+}  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 75a254a..3e55384 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -16,6 +16,7 @@
 #include "gpu/command_buffer/service/context_group.h"
 #include "gpu/command_buffer/service/gl_context_virtual.h"
 #include "gpu/command_buffer/service/gpu_scheduler.h"
+#include "gpu/command_buffer/service/image_manager.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_context.h"
@@ -35,7 +36,9 @@
       share_mailbox_manager(NULL),
       virtual_manager(NULL),
       bind_generates_resource(false),
-      context_lost_allowed(false) {
+      context_lost_allowed(false),
+      image_manager(NULL),
+      image_factory(NULL) {
 }
 
 GLManager::GLManager()
@@ -127,7 +130,7 @@
 
   if (!context_group) {
     context_group = new gles2::ContextGroup(mailbox_manager_.get(),
-                                            NULL,
+                                            options.image_manager,
                                             NULL,
                                             options.bind_generates_resource);
   }
@@ -196,7 +199,8 @@
       client_share_group,
       transfer_buffer_.get(),
       kShareResources,
-      options.bind_generates_resource));
+      options.bind_generates_resource,
+      options.image_factory));
 
   ASSERT_TRUE(gles2_implementation_->Initialize(
       kStartTransferBufferSize,
diff --git a/gpu/command_buffer/tests/gl_manager.h b/gpu/command_buffer/tests/gl_manager.h
index 2ebe695..15fc9ef 100644
--- a/gpu/command_buffer/tests/gl_manager.h
+++ b/gpu/command_buffer/tests/gl_manager.h
@@ -30,6 +30,8 @@
 class GLES2Decoder;
 class GLES2CmdHelper;
 class GLES2Implementation;
+class ImageFactory;
+class ImageManager;
 class ShareGroup;
 
 };
@@ -50,6 +52,10 @@
     bool bind_generates_resource;
     // Whether or not it's ok to lose the context.
     bool context_lost_allowed;
+    // Image manager to be used.
+    gles2::ImageManager* image_manager;
+    // Image factory to be used.
+    gles2::ImageFactory* image_factory;
   };
   GLManager();
   ~GLManager();
diff --git a/gpu/command_buffer/tests/gl_pointcoord_unittest.cc b/gpu/command_buffer/tests/gl_pointcoord_unittest.cc
index 5073ad3..fe71eed 100644
--- a/gpu/command_buffer/tests/gl_pointcoord_unittest.cc
+++ b/gpu/command_buffer/tests/gl_pointcoord_unittest.cc
@@ -101,7 +101,7 @@
   GLint position_loc = glGetAttribLocation(program, "a_position");
   GLint pointsize_loc = glGetUniformLocation(program, "u_pointsize");
 
-  GLint range[2] = { 0, };
+  GLint range[2] = { 0, 0 };
   glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, &range[0]);
   GLint max_point_size = range[1];
   EXPECT_GE(max_point_size, 1);
diff --git a/gpu/command_buffer/tests/gl_tests_main.cc b/gpu/command_buffer/tests/gl_tests_main.cc
index a3a406d..7931cb8 100644
--- a/gpu/command_buffer/tests/gl_tests_main.cc
+++ b/gpu/command_buffer/tests/gl_tests_main.cc
@@ -10,6 +10,7 @@
 #endif
 #include "gpu/command_buffer/client/gles2_lib.h"
 #include "gpu/command_buffer/tests/gl_test_utils.h"
+#include "gpu/config/gpu_util.h"
 #include "ui/gl/gl_surface.h"
 
 #if defined(OS_ANDROID)
@@ -36,6 +37,7 @@
 #endif
   gfx::GLSurface::InitializeOneOff();
   ::gles2::Initialize();
+  gpu::ApplyGpuDriverBugWorkarounds(CommandLine::ForCurrentProcess());
   base::MessageLoop::Type message_loop_type = base::MessageLoop::TYPE_UI;
   base::MessageLoop main_message_loop(message_loop_type);
   return GLTestHelper::RunTests(argc, argv);
diff --git a/gpu/command_buffer_client.target.darwin-arm.mk b/gpu/command_buffer_client.target.darwin-arm.mk
index 8459b33..21565a5 100644
--- a/gpu/command_buffer_client.target.darwin-arm.mk
+++ b/gpu/command_buffer_client.target.darwin-arm.mk
@@ -73,6 +73,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -98,9 +99,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_client.target.darwin-x86.mk b/gpu/command_buffer_client.target.darwin-x86.mk
index 7d9eae5..bb1f905 100644
--- a/gpu/command_buffer_client.target.darwin-x86.mk
+++ b/gpu/command_buffer_client.target.darwin-x86.mk
@@ -75,6 +75,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -100,9 +101,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_client.target.linux-arm.mk b/gpu/command_buffer_client.target.linux-arm.mk
index 8459b33..21565a5 100644
--- a/gpu/command_buffer_client.target.linux-arm.mk
+++ b/gpu/command_buffer_client.target.linux-arm.mk
@@ -73,6 +73,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -98,9 +99,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_client.target.linux-x86.mk b/gpu/command_buffer_client.target.linux-x86.mk
index 7d9eae5..bb1f905 100644
--- a/gpu/command_buffer_client.target.linux-x86.mk
+++ b/gpu/command_buffer_client.target.linux-x86.mk
@@ -75,6 +75,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -100,9 +101,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_common.target.darwin-arm.mk b/gpu/command_buffer_common.target.darwin-arm.mk
index 87d0397..8379f7c 100644
--- a/gpu/command_buffer_common.target.darwin-arm.mk
+++ b/gpu/command_buffer_common.target.darwin-arm.mk
@@ -72,6 +72,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -97,9 +98,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_common.target.darwin-x86.mk b/gpu/command_buffer_common.target.darwin-x86.mk
index 3ba9232..2e39ae3 100644
--- a/gpu/command_buffer_common.target.darwin-x86.mk
+++ b/gpu/command_buffer_common.target.darwin-x86.mk
@@ -74,6 +74,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -99,9 +100,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_common.target.linux-arm.mk b/gpu/command_buffer_common.target.linux-arm.mk
index 87d0397..8379f7c 100644
--- a/gpu/command_buffer_common.target.linux-arm.mk
+++ b/gpu/command_buffer_common.target.linux-arm.mk
@@ -72,6 +72,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -97,9 +98,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_common.target.linux-x86.mk b/gpu/command_buffer_common.target.linux-x86.mk
index 3ba9232..2e39ae3 100644
--- a/gpu/command_buffer_common.target.linux-x86.mk
+++ b/gpu/command_buffer_common.target.linux-x86.mk
@@ -74,6 +74,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -99,9 +100,9 @@
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
 	$(LOCAL_PATH) \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi
index c31e8f2..9e291da 100644
--- a/gpu/command_buffer_service.gypi
+++ b/gpu/command_buffer_service.gypi
@@ -32,6 +32,8 @@
     'command_buffer/service/async_pixel_transfer_delegate_idle.h',
     'command_buffer/service/async_pixel_transfer_delegate_linux.cc',
     'command_buffer/service/async_pixel_transfer_delegate_mac.cc',
+    'command_buffer/service/async_pixel_transfer_delegate_share_group.cc',
+    'command_buffer/service/async_pixel_transfer_delegate_share_group.h',
     'command_buffer/service/async_pixel_transfer_delegate_stub.cc',
     'command_buffer/service/async_pixel_transfer_delegate_stub.h',
     'command_buffer/service/async_pixel_transfer_delegate_sync.cc',
@@ -72,7 +74,6 @@
     'command_buffer/service/gl_state_restorer_impl.cc',
     'command_buffer/service/gl_state_restorer_impl.h',
     'command_buffer/service/gl_utils.h',
-    'command_buffer/service/gpu_driver_bug_workaround_type.h',
     'command_buffer/service/gpu_scheduler.h',
     'command_buffer/service/gpu_scheduler.cc',
     'command_buffer/service/gpu_scheduler_mock.h',
diff --git a/gpu/command_buffer_service.target.darwin-arm.mk b/gpu/command_buffer_service.target.darwin-arm.mk
index f1b7273..270bdfe 100644
--- a/gpu/command_buffer_service.target.darwin-arm.mk
+++ b/gpu/command_buffer_service.target.darwin-arm.mk
@@ -30,6 +30,7 @@
 	gpu/command_buffer/service/async_pixel_transfer_delegate.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc \
+	gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc \
 	gpu/command_buffer/service/buffer_manager.cc \
@@ -112,6 +113,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -147,14 +149,14 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/common \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/i18n \
+	$(PWD)/external/icu4c/common \
+	$(PWD)/external/icu4c/i18n \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
 	$(LOCAL_PATH)/third_party/re2 \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_service.target.darwin-x86.mk b/gpu/command_buffer_service.target.darwin-x86.mk
index e752daf..7c911ab 100644
--- a/gpu/command_buffer_service.target.darwin-x86.mk
+++ b/gpu/command_buffer_service.target.darwin-x86.mk
@@ -30,6 +30,7 @@
 	gpu/command_buffer/service/async_pixel_transfer_delegate.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc \
+	gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc \
 	gpu/command_buffer/service/buffer_manager.cc \
@@ -114,6 +115,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -148,14 +150,14 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/common \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/i18n \
+	$(PWD)/external/icu4c/common \
+	$(PWD)/external/icu4c/i18n \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
 	$(LOCAL_PATH)/third_party/re2 \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_service.target.linux-arm.mk b/gpu/command_buffer_service.target.linux-arm.mk
index f1b7273..270bdfe 100644
--- a/gpu/command_buffer_service.target.linux-arm.mk
+++ b/gpu/command_buffer_service.target.linux-arm.mk
@@ -30,6 +30,7 @@
 	gpu/command_buffer/service/async_pixel_transfer_delegate.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc \
+	gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc \
 	gpu/command_buffer/service/buffer_manager.cc \
@@ -112,6 +113,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -147,14 +149,14 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/common \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/i18n \
+	$(PWD)/external/icu4c/common \
+	$(PWD)/external/icu4c/i18n \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
 	$(LOCAL_PATH)/third_party/re2 \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/command_buffer_service.target.linux-x86.mk b/gpu/command_buffer_service.target.linux-x86.mk
index e752daf..7c911ab 100644
--- a/gpu/command_buffer_service.target.linux-x86.mk
+++ b/gpu/command_buffer_service.target.linux-x86.mk
@@ -30,6 +30,7 @@
 	gpu/command_buffer/service/async_pixel_transfer_delegate.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_android.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc \
+	gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc \
 	gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc \
 	gpu/command_buffer/service/buffer_manager.cc \
@@ -114,6 +115,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -148,14 +150,14 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/common \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/icu4c/i18n \
+	$(PWD)/external/icu4c/common \
+	$(PWD)/external/icu4c/i18n \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
 	$(LOCAL_PATH)/third_party/re2 \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/config/DEPS b/gpu/config/DEPS
new file mode 100644
index 0000000..39b325a
--- /dev/null
+++ b/gpu/config/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+third_party/libxml",  # For parsing WinSAT results files.
+  "+third_party/libXNVCtrl",  # For NV driver version query.
+]
diff --git a/gpu/config/OWNERS b/gpu/config/OWNERS
new file mode 100644
index 0000000..fed3782
--- /dev/null
+++ b/gpu/config/OWNERS
@@ -0,0 +1,5 @@
+apatrick@chromium.org
+kbr@chromium.org
+gman@chromium.org
+piman@chromium.org
+zmo@chromium.org
diff --git a/gpu/config/dx_diag_node.cc b/gpu/config/dx_diag_node.cc
new file mode 100644
index 0000000..e0902ca
--- /dev/null
+++ b/gpu/config/dx_diag_node.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2010 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 "gpu/config/dx_diag_node.h"
+
+namespace gpu {
+
+DxDiagNode::DxDiagNode() {}
+
+DxDiagNode::~DxDiagNode() {}
+
+}  // namespace gpu
diff --git a/gpu/config/dx_diag_node.h b/gpu/config/dx_diag_node.h
new file mode 100644
index 0000000..33d29b3
--- /dev/null
+++ b/gpu/config/dx_diag_node.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 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.
+//
+// A tree of name value pairs that report contain DirectX diagnostic
+// information.
+
+#ifndef GPU_CONFIG_DX_DIAG_NODE_H_
+#define GPU_CONFIG_DX_DIAG_NODE_H_
+
+#include <map>
+#include <string>
+
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+struct GPU_EXPORT DxDiagNode {
+  DxDiagNode();
+  ~DxDiagNode();
+  std::map<std::string, std::string> values;
+  std::map<std::string, DxDiagNode> children;
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_DX_DIAG_NODE_H_
diff --git a/gpu/config/gpu_blacklist.cc b/gpu/config/gpu_blacklist.cc
new file mode 100644
index 0000000..39946b0
--- /dev/null
+++ b/gpu/config/gpu_blacklist.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_blacklist.h"
+
+#include "gpu/config/gpu_feature_type.h"
+
+namespace gpu {
+
+GpuBlacklist::GpuBlacklist()
+    : GpuControlList() {
+}
+
+GpuBlacklist::~GpuBlacklist() {
+}
+
+// static
+GpuBlacklist* GpuBlacklist::Create() {
+  GpuBlacklist* list = new GpuBlacklist();
+  list->AddSupportedFeature("accelerated_2d_canvas",
+                            GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS);
+  list->AddSupportedFeature("accelerated_compositing",
+                            GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING);
+  list->AddSupportedFeature("webgl",
+                            GPU_FEATURE_TYPE_WEBGL);
+  list->AddSupportedFeature("multisampling",
+                            GPU_FEATURE_TYPE_MULTISAMPLING);
+  list->AddSupportedFeature("flash_3d",
+                            GPU_FEATURE_TYPE_FLASH3D);
+  list->AddSupportedFeature("flash_stage3d",
+                            GPU_FEATURE_TYPE_FLASH_STAGE3D);
+  list->AddSupportedFeature("flash_stage3d_baseline",
+                            GPU_FEATURE_TYPE_FLASH_STAGE3D_BASELINE);
+  list->AddSupportedFeature("texture_sharing",
+                            GPU_FEATURE_TYPE_TEXTURE_SHARING);
+  list->AddSupportedFeature("accelerated_video_decode",
+                            GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE);
+  list->AddSupportedFeature("3d_css",
+                            GPU_FEATURE_TYPE_3D_CSS);
+  list->AddSupportedFeature("accelerated_video",
+                            GPU_FEATURE_TYPE_ACCELERATED_VIDEO);
+  list->AddSupportedFeature("panel_fitting",
+                            GPU_FEATURE_TYPE_PANEL_FITTING);
+  list->AddSupportedFeature("force_compositing_mode",
+                            GPU_FEATURE_TYPE_FORCE_COMPOSITING_MODE);
+  list->set_supports_feature_type_all(true);
+  return list;
+}
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_blacklist.h b/gpu/config/gpu_blacklist.h
new file mode 100644
index 0000000..4311212
--- /dev/null
+++ b/gpu/config/gpu_blacklist.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 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 GPU_CONFIG_GPU_BLACKLIST_H_
+#define GPU_CONFIG_GPU_BLACKLIST_H_
+
+#include <string>
+
+#include "gpu/config/gpu_control_list.h"
+
+namespace gpu {
+
+class GPU_EXPORT GpuBlacklist : public GpuControlList {
+ public:
+  virtual ~GpuBlacklist();
+
+  static GpuBlacklist* Create();
+
+ private:
+  GpuBlacklist();
+
+  DISALLOW_COPY_AND_ASSIGN(GpuBlacklist);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_BLACKLIST_H_
diff --git a/gpu/config/gpu_blacklist_unittest.cc b/gpu/config/gpu_blacklist_unittest.cc
new file mode 100644
index 0000000..3282437
--- /dev/null
+++ b/gpu/config/gpu_blacklist_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2013 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/scoped_ptr.h"
+#include "gpu/config/gpu_blacklist.h"
+#include "gpu/config/gpu_control_list_jsons.h"
+#include "gpu/config/gpu_feature_type.h"
+#include "gpu/config/gpu_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const char kOsVersion[] = "10.6.4";
+
+namespace gpu {
+
+class GpuBlacklistTest : public testing::Test {
+ public:
+  GpuBlacklistTest() { }
+
+  virtual ~GpuBlacklistTest() { }
+
+  const GPUInfo& gpu_info() const {
+    return gpu_info_;
+  }
+
+  void RunFeatureTest(
+      const std::string feature_name, GpuFeatureType feature_type) {
+    const std::string json =
+        "{\n"
+        "  \"name\": \"gpu blacklist\",\n"
+        "  \"version\": \"0.1\",\n"
+        "  \"entries\": [\n"
+        "    {\n"
+        "      \"id\": 1,\n"
+        "      \"os\": {\n"
+        "        \"type\": \"macosx\"\n"
+        "      },\n"
+        "      \"vendor_id\": \"0x10de\",\n"
+        "      \"device_id\": [\"0x0640\"],\n"
+        "      \"features\": [\n"
+        "        \"" +
+        feature_name +
+        "\"\n"
+        "      ]\n"
+        "    }\n"
+        "  ]\n"
+        "}";
+
+    scoped_ptr<GpuBlacklist> blacklist(GpuBlacklist::Create());
+    EXPECT_TRUE(blacklist->LoadList(json, GpuBlacklist::kAllOs));
+    std::set<int> type = blacklist->MakeDecision(
+        GpuBlacklist::kOsMacosx, kOsVersion, gpu_info());
+    EXPECT_EQ(1u, type.size());
+    EXPECT_EQ(1u, type.count(feature_type));
+  }
+
+ protected:
+  virtual void SetUp() {
+    gpu_info_.gpu.vendor_id = 0x10de;
+    gpu_info_.gpu.device_id = 0x0640;
+    gpu_info_.driver_vendor = "NVIDIA";
+    gpu_info_.driver_version = "1.6.18";
+    gpu_info_.driver_date = "7-14-2009";
+    gpu_info_.machine_model = "MacBookPro 7.1";
+    gpu_info_.gl_vendor = "NVIDIA Corporation";
+    gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
+    gpu_info_.performance_stats.graphics = 5.0;
+    gpu_info_.performance_stats.gaming = 5.0;
+    gpu_info_.performance_stats.overall = 5.0;
+  }
+
+  virtual void TearDown() {
+  }
+
+ private:
+  GPUInfo gpu_info_;
+};
+
+TEST_F(GpuBlacklistTest, CurrentBlacklistValidation) {
+  scoped_ptr<GpuBlacklist> blacklist(GpuBlacklist::Create());
+  EXPECT_TRUE(blacklist->LoadList(
+      kSoftwareRenderingListJson, GpuBlacklist::kAllOs));
+  EXPECT_FALSE(blacklist->contains_unknown_fields());
+}
+
+#define GPU_BLACKLIST_FEATURE_TEST(test_name, feature_name, feature_type) \
+TEST_F(GpuBlacklistTest, test_name) {                                     \
+  RunFeatureTest(feature_name, feature_type);                             \
+}
+
+GPU_BLACKLIST_FEATURE_TEST(Accelerated2DCanvas,
+                           "accelerated_2d_canvas",
+                           GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)
+
+GPU_BLACKLIST_FEATURE_TEST(AcceleratedCompositing,
+                           "accelerated_compositing",
+                           GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING)
+
+GPU_BLACKLIST_FEATURE_TEST(WebGL,
+                           "webgl",
+                           GPU_FEATURE_TYPE_WEBGL)
+
+GPU_BLACKLIST_FEATURE_TEST(Multisampling,
+                           "multisampling",
+                           GPU_FEATURE_TYPE_MULTISAMPLING)
+
+GPU_BLACKLIST_FEATURE_TEST(Flash3D,
+                           "flash_3d",
+                           GPU_FEATURE_TYPE_FLASH3D)
+
+GPU_BLACKLIST_FEATURE_TEST(FlashStage3D,
+                           "flash_stage3d",
+                           GPU_FEATURE_TYPE_FLASH_STAGE3D)
+
+GPU_BLACKLIST_FEATURE_TEST(FlashStage3DBaseline,
+                           "flash_stage3d_baseline",
+                           GPU_FEATURE_TYPE_FLASH_STAGE3D_BASELINE)
+
+GPU_BLACKLIST_FEATURE_TEST(TextureSharing,
+                           "texture_sharing",
+                           GPU_FEATURE_TYPE_TEXTURE_SHARING)
+
+GPU_BLACKLIST_FEATURE_TEST(AcceleratedVideoDecode,
+                           "accelerated_video_decode",
+                           GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE)
+
+GPU_BLACKLIST_FEATURE_TEST(Css3D,
+                           "3d_css",
+                           GPU_FEATURE_TYPE_3D_CSS)
+
+GPU_BLACKLIST_FEATURE_TEST(AcceleratedVideo,
+                           "accelerated_video",
+                           GPU_FEATURE_TYPE_ACCELERATED_VIDEO)
+
+GPU_BLACKLIST_FEATURE_TEST(PanelFitting,
+                           "panel_fitting",
+                           GPU_FEATURE_TYPE_PANEL_FITTING)
+
+GPU_BLACKLIST_FEATURE_TEST(ForceCompositingMode,
+                           "force_compositing_mode",
+                           GPU_FEATURE_TYPE_FORCE_COMPOSITING_MODE)
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc
new file mode 100644
index 0000000..670ccff
--- /dev/null
+++ b/gpu/config/gpu_control_list.cc
@@ -0,0 +1,1415 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_control_list.h"
+
+#include "base/cpu.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/sys_info.h"
+#include "gpu/config/gpu_info.h"
+#include "gpu/config/gpu_util.h"
+
+namespace gpu {
+namespace {
+
+// Break a version string into segments.  Return true if each segment is
+// a valid number.
+bool ProcessVersionString(const std::string& version_string,
+                          char splitter,
+                          std::vector<std::string>* version) {
+  DCHECK(version);
+  base::SplitString(version_string, splitter, version);
+  if (version->size() == 0)
+    return false;
+  // If the splitter is '-', we assume it's a date with format "mm-dd-yyyy";
+  // we split it into the order of "yyyy", "mm", "dd".
+  if (splitter == '-') {
+    std::string year = (*version)[version->size() - 1];
+    for (int i = version->size() - 1; i > 0; --i) {
+      (*version)[i] = (*version)[i - 1];
+    }
+    (*version)[0] = year;
+  }
+  for (size_t i = 0; i < version->size(); ++i) {
+    unsigned num = 0;
+    if (!base::StringToUint((*version)[i], &num))
+      return false;
+  }
+  return true;
+}
+
+// Compare two number strings using numerical ordering.
+// Return  0 if number = number_ref,
+//         1 if number > number_ref,
+//        -1 if number < number_ref.
+int CompareNumericalNumberStrings(
+    const std::string& number, const std::string& number_ref) {
+  unsigned value1 = 0;
+  unsigned value2 = 0;
+  bool valid = base::StringToUint(number, &value1);
+  DCHECK(valid);
+  valid = base::StringToUint(number_ref, &value2);
+  DCHECK(valid);
+  if (value1 == value2)
+    return 0;
+  if (value1 > value2)
+    return 1;
+  return -1;
+}
+
+// Compare two number strings using lexical ordering.
+// Return  0 if number = number_ref,
+//         1 if number > number_ref,
+//        -1 if number < number_ref.
+// We only compare as many digits as number_ref contains.
+// If number_ref is xxx, it's considered as xxx*
+// For example: CompareLexicalNumberStrings("121", "12") returns 0,
+//              CompareLexicalNumberStrings("12", "121") returns -1.
+int CompareLexicalNumberStrings(
+    const std::string& number, const std::string& number_ref) {
+  for (size_t i = 0; i < number_ref.length(); ++i) {
+    unsigned value1 = 0;
+    if (i < number.length())
+      value1 = number[i] - '0';
+    unsigned value2 = number_ref[i] - '0';
+    if (value1 > value2)
+      return 1;
+    if (value1 < value2)
+      return -1;
+  }
+  return 0;
+}
+
+bool GpuUnmatched(uint32 vendor_id, const std::vector<uint32>& device_id_list,
+                  const GPUInfo::GPUDevice& gpu) {
+  if (vendor_id == 0)
+    return false;
+  if (vendor_id != gpu.vendor_id)
+    return true;
+  bool device_specified = false;
+  for (size_t i = 0; i < device_id_list.size(); ++i) {
+    if (device_id_list[i] == 0)
+      continue;
+    if (device_id_list[i] == gpu.device_id)
+      return false;
+    device_specified = true;
+  }
+  return device_specified;
+}
+
+const char kMultiGpuStyleStringAMDSwitchable[] = "amd_switchable";
+const char kMultiGpuStyleStringOptimus[] = "optimus";
+
+const char kMultiGpuCategoryStringPrimary[] = "primary";
+const char kMultiGpuCategoryStringSecondary[] = "secondary";
+const char kMultiGpuCategoryStringAny[] = "any";
+
+const char kVersionStyleStringNumerical[] = "numerical";
+const char kVersionStyleStringLexical[] = "lexical";
+
+const char kOp[] = "op";
+
+}  // namespace anonymous
+
+GpuControlList::VersionInfo::VersionInfo(
+    const std::string& version_op,
+    const std::string& version_style,
+    const std::string& version_string,
+    const std::string& version_string2)
+    : version_style_(kVersionStyleNumerical) {
+  op_ = StringToNumericOp(version_op);
+  if (op_ == kUnknown || op_ == kAny)
+    return;
+  version_style_ = StringToVersionStyle(version_style);
+  if (!ProcessVersionString(version_string, '.', &version_)) {
+    op_ = kUnknown;
+    return;
+  }
+  if (op_ == kBetween) {
+    if (!ProcessVersionString(version_string2, '.', &version2_))
+      op_ = kUnknown;
+  }
+}
+
+GpuControlList::VersionInfo::~VersionInfo() {
+}
+
+bool GpuControlList::VersionInfo::Contains(
+    const std::string& version_string) const {
+  return Contains(version_string, '.');
+}
+
+bool GpuControlList::VersionInfo::Contains(
+    const std::string& version_string, char splitter) const {
+  if (op_ == kUnknown)
+    return false;
+  if (op_ == kAny)
+    return true;
+  std::vector<std::string> version;
+  if (!ProcessVersionString(version_string, splitter, &version))
+    return false;
+  int relation = Compare(version, version_, version_style_);
+  if (op_ == kEQ)
+    return (relation == 0);
+  else if (op_ == kLT)
+    return (relation < 0);
+  else if (op_ == kLE)
+    return (relation <= 0);
+  else if (op_ == kGT)
+    return (relation > 0);
+  else if (op_ == kGE)
+    return (relation >= 0);
+  // op_ == kBetween
+  if (relation < 0)
+    return false;
+  return Compare(version, version2_, version_style_) <= 0;
+}
+
+bool GpuControlList::VersionInfo::IsValid() const {
+  return (op_ != kUnknown && version_style_ != kVersionStyleUnknown);
+}
+
+bool GpuControlList::VersionInfo::IsLexical() const {
+  return version_style_ == kVersionStyleLexical;
+}
+
+// static
+int GpuControlList::VersionInfo::Compare(
+    const std::vector<std::string>& version,
+    const std::vector<std::string>& version_ref,
+    VersionStyle version_style) {
+  DCHECK(version.size() > 0 && version_ref.size() > 0);
+  DCHECK(version_style != kVersionStyleUnknown);
+  for (size_t i = 0; i < version_ref.size(); ++i) {
+    if (i >= version.size())
+      return 0;
+    int ret = 0;
+    // We assume both versions are checked by ProcessVersionString().
+    if (i > 0 && version_style == kVersionStyleLexical)
+      ret = CompareLexicalNumberStrings(version[i], version_ref[i]);
+    else
+      ret = CompareNumericalNumberStrings(version[i], version_ref[i]);
+    if (ret != 0)
+      return ret;
+  }
+  return 0;
+}
+
+// static
+GpuControlList::VersionInfo::VersionStyle
+GpuControlList::VersionInfo::StringToVersionStyle(
+    const std::string& version_style) {
+  if (version_style.empty() || version_style == kVersionStyleStringNumerical)
+    return kVersionStyleNumerical;
+  if (version_style == kVersionStyleStringLexical)
+    return kVersionStyleLexical;
+  return kVersionStyleUnknown;
+}
+
+GpuControlList::OsInfo::OsInfo(const std::string& os,
+                             const std::string& version_op,
+                             const std::string& version_string,
+                             const std::string& version_string2) {
+  type_ = StringToOsType(os);
+  if (type_ != kOsUnknown) {
+    version_info_.reset(new VersionInfo(
+        version_op, std::string(), version_string, version_string2));
+  }
+}
+
+GpuControlList::OsInfo::~OsInfo() {}
+
+bool GpuControlList::OsInfo::Contains(OsType type,
+                                    const std::string& version) const {
+  if (!IsValid())
+    return false;
+  if (type_ != type && type_ != kOsAny)
+    return false;
+  return version_info_->Contains(version);
+}
+
+bool GpuControlList::OsInfo::IsValid() const {
+  return type_ != kOsUnknown && version_info_->IsValid();
+}
+
+GpuControlList::OsType GpuControlList::OsInfo::type() const {
+  return type_;
+}
+
+GpuControlList::OsType GpuControlList::OsInfo::StringToOsType(
+    const std::string& os) {
+  if (os == "win")
+    return kOsWin;
+  else if (os == "macosx")
+    return kOsMacosx;
+  else if (os == "android")
+    return kOsAndroid;
+  else if (os == "linux")
+    return kOsLinux;
+  else if (os == "chromeos")
+    return kOsChromeOS;
+  else if (os == "any")
+    return kOsAny;
+  return kOsUnknown;
+}
+
+GpuControlList::MachineModelInfo::MachineModelInfo(
+    const std::string& name_op,
+    const std::string& name_value,
+    const std::string& version_op,
+    const std::string& version_string,
+    const std::string& version_string2) {
+  name_info_.reset(new StringInfo(name_op, name_value));
+  version_info_.reset(new VersionInfo(
+      version_op, std::string(), version_string, version_string2));
+}
+
+GpuControlList::MachineModelInfo::~MachineModelInfo() {}
+
+bool GpuControlList::MachineModelInfo::Contains(
+    const std::string& name, const std::string& version) const {
+  if (!IsValid())
+    return false;
+  if (!name_info_->Contains(name))
+    return false;
+  return version_info_->Contains(version);
+}
+
+bool GpuControlList::MachineModelInfo::IsValid() const {
+  return name_info_->IsValid() && version_info_->IsValid();
+}
+
+GpuControlList::StringInfo::StringInfo(const std::string& string_op,
+                                     const std::string& string_value) {
+  op_ = StringToOp(string_op);
+  value_ = StringToLowerASCII(string_value);
+}
+
+bool GpuControlList::StringInfo::Contains(const std::string& value) const {
+  std::string my_value = StringToLowerASCII(value);
+  switch (op_) {
+    case kContains:
+      return strstr(my_value.c_str(), value_.c_str()) != NULL;
+    case kBeginWith:
+      return StartsWithASCII(my_value, value_, false);
+    case kEndWith:
+      return EndsWith(my_value, value_, false);
+    case kEQ:
+      return value_ == my_value;
+    default:
+      return false;
+  }
+}
+
+bool GpuControlList::StringInfo::IsValid() const {
+  return op_ != kUnknown;
+}
+
+GpuControlList::StringInfo::Op GpuControlList::StringInfo::StringToOp(
+    const std::string& string_op) {
+  if (string_op == "=")
+    return kEQ;
+  else if (string_op == "contains")
+    return kContains;
+  else if (string_op == "beginwith")
+    return kBeginWith;
+  else if (string_op == "endwith")
+    return kEndWith;
+  return kUnknown;
+}
+
+GpuControlList::FloatInfo::FloatInfo(const std::string& float_op,
+                                     const std::string& float_value,
+                                     const std::string& float_value2)
+    : op_(kUnknown),
+      value_(0.f),
+      value2_(0.f) {
+  op_ = StringToNumericOp(float_op);
+  if (op_ == kAny)
+    return;
+  double dvalue = 0;
+  if (!base::StringToDouble(float_value, &dvalue)) {
+    op_ = kUnknown;
+    return;
+  }
+  value_ = static_cast<float>(dvalue);
+  if (op_ == kBetween) {
+    if (!base::StringToDouble(float_value2, &dvalue)) {
+      op_ = kUnknown;
+      return;
+    }
+    value2_ = static_cast<float>(dvalue);
+  }
+}
+
+bool GpuControlList::FloatInfo::Contains(float value) const {
+  if (op_ == kUnknown)
+    return false;
+  if (op_ == kAny)
+    return true;
+  if (op_ == kEQ)
+    return (value == value_);
+  if (op_ == kLT)
+    return (value < value_);
+  if (op_ == kLE)
+    return (value <= value_);
+  if (op_ == kGT)
+    return (value > value_);
+  if (op_ == kGE)
+    return (value >= value_);
+  DCHECK(op_ == kBetween);
+  return ((value_ <= value && value <= value2_) ||
+          (value2_ <= value && value <= value_));
+}
+
+bool GpuControlList::FloatInfo::IsValid() const {
+  return op_ != kUnknown;
+}
+
+GpuControlList::IntInfo::IntInfo(const std::string& int_op,
+                                 const std::string& int_value,
+                                 const std::string& int_value2)
+    : op_(kUnknown),
+      value_(0),
+      value2_(0) {
+  op_ = StringToNumericOp(int_op);
+  if (op_ == kAny)
+    return;
+  if (!base::StringToInt(int_value, &value_)) {
+    op_ = kUnknown;
+    return;
+  }
+  if (op_ == kBetween &&
+      !base::StringToInt(int_value2, &value2_))
+    op_ = kUnknown;
+}
+
+bool GpuControlList::IntInfo::Contains(int value) const {
+  if (op_ == kUnknown)
+    return false;
+  if (op_ == kAny)
+    return true;
+  if (op_ == kEQ)
+    return (value == value_);
+  if (op_ == kLT)
+    return (value < value_);
+  if (op_ == kLE)
+    return (value <= value_);
+  if (op_ == kGT)
+    return (value > value_);
+  if (op_ == kGE)
+    return (value >= value_);
+  DCHECK(op_ == kBetween);
+  return ((value_ <= value && value <= value2_) ||
+          (value2_ <= value && value <= value_));
+}
+
+bool GpuControlList::IntInfo::IsValid() const {
+  return op_ != kUnknown;
+}
+
+// static
+GpuControlList::ScopedGpuControlListEntry
+GpuControlList::GpuControlListEntry::GetEntryFromValue(
+    const base::DictionaryValue* value, bool top_level,
+    const FeatureMap& feature_map,
+    bool supports_feature_type_all) {
+  DCHECK(value);
+  ScopedGpuControlListEntry entry(new GpuControlListEntry());
+
+  size_t dictionary_entry_count = 0;
+
+  if (top_level) {
+    uint32 id;
+    if (!value->GetInteger("id", reinterpret_cast<int*>(&id)) ||
+        !entry->SetId(id)) {
+      LOG(WARNING) << "Malformed id entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+
+    bool disabled;
+    if (value->GetBoolean("disabled", &disabled)) {
+      entry->SetDisabled(disabled);
+      dictionary_entry_count++;
+    }
+  }
+
+  std::string description;
+  if (value->GetString("description", &description)) {
+    entry->description_ = description;
+    dictionary_entry_count++;
+  } else {
+    entry->description_ = "The GPU is unavailable for an unexplained reason.";
+  }
+
+  const base::ListValue* cr_bugs;
+  if (value->GetList("cr_bugs", &cr_bugs)) {
+    for (size_t i = 0; i < cr_bugs->GetSize(); ++i) {
+      int bug_id;
+      if (cr_bugs->GetInteger(i, &bug_id)) {
+        entry->cr_bugs_.push_back(bug_id);
+      } else {
+        LOG(WARNING) << "Malformed cr_bugs entry " << entry->id();
+        return NULL;
+      }
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::ListValue* webkit_bugs;
+  if (value->GetList("webkit_bugs", &webkit_bugs)) {
+    for (size_t i = 0; i < webkit_bugs->GetSize(); ++i) {
+      int bug_id;
+      if (webkit_bugs->GetInteger(i, &bug_id)) {
+        entry->webkit_bugs_.push_back(bug_id);
+      } else {
+        LOG(WARNING) << "Malformed webkit_bugs entry " << entry->id();
+        return NULL;
+      }
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* os_value = NULL;
+  if (value->GetDictionary("os", &os_value)) {
+    std::string os_type;
+    std::string os_version_op = "any";
+    std::string os_version_string;
+    std::string os_version_string2;
+    os_value->GetString("type", &os_type);
+    const base::DictionaryValue* os_version_value = NULL;
+    if (os_value->GetDictionary("version", &os_version_value)) {
+      os_version_value->GetString(kOp, &os_version_op);
+      os_version_value->GetString("number", &os_version_string);
+      os_version_value->GetString("number2", &os_version_string2);
+    }
+    if (!entry->SetOsInfo(os_type, os_version_op, os_version_string,
+                          os_version_string2)) {
+      LOG(WARNING) << "Malformed os entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  std::string vendor_id;
+  if (value->GetString("vendor_id", &vendor_id)) {
+    if (!entry->SetVendorId(vendor_id)) {
+      LOG(WARNING) << "Malformed vendor_id entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::ListValue* device_id_list;
+  if (value->GetList("device_id", &device_id_list)) {
+    for (size_t i = 0; i < device_id_list->GetSize(); ++i) {
+        std::string device_id;
+      if (!device_id_list->GetString(i, &device_id) ||
+          !entry->AddDeviceId(device_id)) {
+        LOG(WARNING) << "Malformed device_id entry " << entry->id();
+        return NULL;
+      }
+    }
+    dictionary_entry_count++;
+  }
+
+  std::string multi_gpu_style;
+  if (value->GetString("multi_gpu_style", &multi_gpu_style)) {
+    if (!entry->SetMultiGpuStyle(multi_gpu_style)) {
+      LOG(WARNING) << "Malformed multi_gpu_style entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  std::string multi_gpu_category;
+  if (value->GetString("multi_gpu_category", &multi_gpu_category)) {
+    if (!entry->SetMultiGpuCategory(multi_gpu_category)) {
+      LOG(WARNING) << "Malformed multi_gpu_category entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* driver_vendor_value = NULL;
+  if (value->GetDictionary("driver_vendor", &driver_vendor_value)) {
+    std::string vendor_op;
+    std::string vendor_value;
+    driver_vendor_value->GetString(kOp, &vendor_op);
+    driver_vendor_value->GetString("value", &vendor_value);
+    if (!entry->SetDriverVendorInfo(vendor_op, vendor_value)) {
+      LOG(WARNING) << "Malformed driver_vendor entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* driver_version_value = NULL;
+  if (value->GetDictionary("driver_version", &driver_version_value)) {
+    std::string driver_version_op = "any";
+    std::string driver_version_style;
+    std::string driver_version_string;
+    std::string driver_version_string2;
+    driver_version_value->GetString(kOp, &driver_version_op);
+    driver_version_value->GetString("style", &driver_version_style);
+    driver_version_value->GetString("number", &driver_version_string);
+    driver_version_value->GetString("number2", &driver_version_string2);
+    if (!entry->SetDriverVersionInfo(driver_version_op,
+                                     driver_version_style,
+                                     driver_version_string,
+                                     driver_version_string2)) {
+      LOG(WARNING) << "Malformed driver_version entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* driver_date_value = NULL;
+  if (value->GetDictionary("driver_date", &driver_date_value)) {
+    std::string driver_date_op = "any";
+    std::string driver_date_string;
+    std::string driver_date_string2;
+    driver_date_value->GetString(kOp, &driver_date_op);
+    driver_date_value->GetString("number", &driver_date_string);
+    driver_date_value->GetString("number2", &driver_date_string2);
+    if (!entry->SetDriverDateInfo(driver_date_op, driver_date_string,
+                                  driver_date_string2)) {
+      LOG(WARNING) << "Malformed driver_date entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* gl_vendor_value = NULL;
+  if (value->GetDictionary("gl_vendor", &gl_vendor_value)) {
+    std::string vendor_op;
+    std::string vendor_value;
+    gl_vendor_value->GetString(kOp, &vendor_op);
+    gl_vendor_value->GetString("value", &vendor_value);
+    if (!entry->SetGLVendorInfo(vendor_op, vendor_value)) {
+      LOG(WARNING) << "Malformed gl_vendor entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* gl_renderer_value = NULL;
+  if (value->GetDictionary("gl_renderer", &gl_renderer_value)) {
+    std::string renderer_op;
+    std::string renderer_value;
+    gl_renderer_value->GetString(kOp, &renderer_op);
+    gl_renderer_value->GetString("value", &renderer_value);
+    if (!entry->SetGLRendererInfo(renderer_op, renderer_value)) {
+      LOG(WARNING) << "Malformed gl_renderer entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* gl_extensions_value = NULL;
+  if (value->GetDictionary("gl_extensions", &gl_extensions_value)) {
+    std::string extensions_op;
+    std::string extensions_value;
+    gl_extensions_value->GetString(kOp, &extensions_op);
+    gl_extensions_value->GetString("value", &extensions_value);
+    if (!entry->SetGLExtensionsInfo(extensions_op, extensions_value)) {
+      LOG(WARNING) << "Malformed gl_extensions entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* cpu_brand_value = NULL;
+  if (value->GetDictionary("cpu_info", &cpu_brand_value)) {
+    std::string cpu_op;
+    std::string cpu_value;
+    cpu_brand_value->GetString(kOp, &cpu_op);
+    cpu_brand_value->GetString("value", &cpu_value);
+    if (!entry->SetCpuBrand(cpu_op, cpu_value)) {
+      LOG(WARNING) << "Malformed cpu_brand entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* perf_graphics_value = NULL;
+  if (value->GetDictionary("perf_graphics", &perf_graphics_value)) {
+    std::string op;
+    std::string float_value;
+    std::string float_value2;
+    perf_graphics_value->GetString(kOp, &op);
+    perf_graphics_value->GetString("value", &float_value);
+    perf_graphics_value->GetString("value2", &float_value2);
+    if (!entry->SetPerfGraphicsInfo(op, float_value, float_value2)) {
+      LOG(WARNING) << "Malformed perf_graphics entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* perf_gaming_value = NULL;
+  if (value->GetDictionary("perf_gaming", &perf_gaming_value)) {
+    std::string op;
+    std::string float_value;
+    std::string float_value2;
+    perf_gaming_value->GetString(kOp, &op);
+    perf_gaming_value->GetString("value", &float_value);
+    perf_gaming_value->GetString("value2", &float_value2);
+    if (!entry->SetPerfGamingInfo(op, float_value, float_value2)) {
+      LOG(WARNING) << "Malformed perf_gaming entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* perf_overall_value = NULL;
+  if (value->GetDictionary("perf_overall", &perf_overall_value)) {
+    std::string op;
+    std::string float_value;
+    std::string float_value2;
+    perf_overall_value->GetString(kOp, &op);
+    perf_overall_value->GetString("value", &float_value);
+    perf_overall_value->GetString("value2", &float_value2);
+    if (!entry->SetPerfOverallInfo(op, float_value, float_value2)) {
+      LOG(WARNING) << "Malformed perf_overall entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* machine_model_value = NULL;
+  if (value->GetDictionary("machine_model", &machine_model_value)) {
+    std::string name_op;
+    std::string name_value;
+    const base::DictionaryValue* name = NULL;
+    if (machine_model_value->GetDictionary("name", &name)) {
+      name->GetString(kOp, &name_op);
+      name->GetString("value", &name_value);
+    }
+
+    std::string version_op = "any";
+    std::string version_string;
+    std::string version_string2;
+    const base::DictionaryValue* version_value = NULL;
+    if (machine_model_value->GetDictionary("version", &version_value)) {
+      version_value->GetString(kOp, &version_op);
+      version_value->GetString("number", &version_string);
+      version_value->GetString("number2", &version_string2);
+    }
+    if (!entry->SetMachineModelInfo(
+            name_op, name_value, version_op, version_string, version_string2)) {
+      LOG(WARNING) << "Malformed machine_model entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  const base::DictionaryValue* gpu_count_value = NULL;
+  if (value->GetDictionary("gpu_count", &gpu_count_value)) {
+    std::string op;
+    std::string int_value;
+    std::string int_value2;
+    gpu_count_value->GetString(kOp, &op);
+    gpu_count_value->GetString("value", &int_value);
+    gpu_count_value->GetString("value2", &int_value2);
+    if (!entry->SetGpuCountInfo(op, int_value, int_value2)) {
+      LOG(WARNING) << "Malformed gpu_count entry " << entry->id();
+      return NULL;
+    }
+    dictionary_entry_count++;
+  }
+
+  if (top_level) {
+    const base::ListValue* feature_value = NULL;
+    if (value->GetList("features", &feature_value)) {
+      std::vector<std::string> feature_list;
+      for (size_t i = 0; i < feature_value->GetSize(); ++i) {
+        std::string feature;
+        if (feature_value->GetString(i, &feature)) {
+          feature_list.push_back(feature);
+        } else {
+          LOG(WARNING) << "Malformed feature entry " << entry->id();
+          return NULL;
+        }
+      }
+      if (!entry->SetFeatures(
+              feature_list, feature_map, supports_feature_type_all)) {
+        LOG(WARNING) << "Malformed feature entry " << entry->id();
+        return NULL;
+      }
+      dictionary_entry_count++;
+    }
+  }
+
+  if (top_level) {
+    const base::ListValue* exception_list_value = NULL;
+    if (value->GetList("exceptions", &exception_list_value)) {
+      for (size_t i = 0; i < exception_list_value->GetSize(); ++i) {
+        const base::DictionaryValue* exception_value = NULL;
+        if (!exception_list_value->GetDictionary(i, &exception_value)) {
+          LOG(WARNING) << "Malformed exceptions entry " << entry->id();
+          return NULL;
+        }
+        ScopedGpuControlListEntry exception(GetEntryFromValue(
+            exception_value, false, feature_map, supports_feature_type_all));
+        if (exception == NULL) {
+          LOG(WARNING) << "Malformed exceptions entry " << entry->id();
+          return NULL;
+        }
+        if (exception->contains_unknown_fields_) {
+          LOG(WARNING) << "Exception with unknown fields " << entry->id();
+          entry->contains_unknown_fields_ = true;
+        } else {
+          entry->AddException(exception);
+        }
+      }
+      dictionary_entry_count++;
+    }
+
+    const base::DictionaryValue* browser_version_value = NULL;
+    // browser_version is processed in LoadGpuControlList().
+    if (value->GetDictionary("browser_version", &browser_version_value))
+      dictionary_entry_count++;
+  }
+
+  if (value->size() != dictionary_entry_count) {
+    LOG(WARNING) << "Entry with unknown fields " << entry->id();
+    entry->contains_unknown_fields_ = true;
+  }
+  return entry;
+}
+
+GpuControlList::GpuControlListEntry::GpuControlListEntry()
+    : id_(0),
+      disabled_(false),
+      vendor_id_(0),
+      multi_gpu_style_(kMultiGpuStyleNone),
+      multi_gpu_category_(kMultiGpuCategoryPrimary),
+      contains_unknown_fields_(false),
+      contains_unknown_features_(false) {
+}
+
+GpuControlList::GpuControlListEntry::~GpuControlListEntry() { }
+
+bool GpuControlList::GpuControlListEntry::SetId(uint32 id) {
+  if (id != 0) {
+    id_ = id;
+    return true;
+  }
+  return false;
+}
+
+void GpuControlList::GpuControlListEntry::SetDisabled(bool disabled) {
+  disabled_ = disabled;
+}
+
+bool GpuControlList::GpuControlListEntry::SetOsInfo(
+    const std::string& os,
+    const std::string& version_op,
+    const std::string& version_string,
+    const std::string& version_string2) {
+  os_info_.reset(new OsInfo(os, version_op, version_string, version_string2));
+  return os_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetVendorId(
+    const std::string& vendor_id_string) {
+  vendor_id_ = 0;
+  return base::HexStringToInt(vendor_id_string,
+                              reinterpret_cast<int*>(&vendor_id_));
+}
+
+bool GpuControlList::GpuControlListEntry::AddDeviceId(
+    const std::string& device_id_string) {
+  uint32 device_id = 0;
+  if (base::HexStringToInt(device_id_string,
+                           reinterpret_cast<int*>(&device_id))) {
+    device_id_list_.push_back(device_id);
+    return true;
+  }
+  return false;
+}
+
+bool GpuControlList::GpuControlListEntry::SetMultiGpuStyle(
+    const std::string& multi_gpu_style_string) {
+  MultiGpuStyle style = StringToMultiGpuStyle(multi_gpu_style_string);
+  if (style == kMultiGpuStyleNone)
+    return false;
+  multi_gpu_style_ = style;
+  return true;
+}
+
+bool GpuControlList::GpuControlListEntry::SetMultiGpuCategory(
+    const std::string& multi_gpu_category_string) {
+  MultiGpuCategory category =
+      StringToMultiGpuCategory(multi_gpu_category_string);
+  if (category == kMultiGpuCategoryNone)
+    return false;
+  multi_gpu_category_ = category;
+  return true;
+}
+
+bool GpuControlList::GpuControlListEntry::SetDriverVendorInfo(
+    const std::string& vendor_op,
+    const std::string& vendor_value) {
+  driver_vendor_info_.reset(new StringInfo(vendor_op, vendor_value));
+  return driver_vendor_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetDriverVersionInfo(
+    const std::string& version_op,
+    const std::string& version_style,
+    const std::string& version_string,
+    const std::string& version_string2) {
+  driver_version_info_.reset(new VersionInfo(
+      version_op, version_style, version_string, version_string2));
+  return driver_version_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetDriverDateInfo(
+    const std::string& date_op,
+    const std::string& date_string,
+    const std::string& date_string2) {
+  driver_date_info_.reset(
+      new VersionInfo(date_op, std::string(), date_string, date_string2));
+  return driver_date_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetGLVendorInfo(
+    const std::string& vendor_op,
+    const std::string& vendor_value) {
+  gl_vendor_info_.reset(new StringInfo(vendor_op, vendor_value));
+  return gl_vendor_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetGLRendererInfo(
+    const std::string& renderer_op,
+    const std::string& renderer_value) {
+  gl_renderer_info_.reset(new StringInfo(renderer_op, renderer_value));
+  return gl_renderer_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetGLExtensionsInfo(
+    const std::string& extensions_op,
+    const std::string& extensions_value) {
+  gl_extensions_info_.reset(new StringInfo(extensions_op, extensions_value));
+  return gl_extensions_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetCpuBrand(
+    const std::string& cpu_op,
+    const std::string& cpu_value) {
+  cpu_brand_.reset(new StringInfo(cpu_op, cpu_value));
+  return cpu_brand_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetPerfGraphicsInfo(
+    const std::string& op,
+    const std::string& float_string,
+    const std::string& float_string2) {
+  perf_graphics_info_.reset(new FloatInfo(op, float_string, float_string2));
+  return perf_graphics_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetPerfGamingInfo(
+    const std::string& op,
+    const std::string& float_string,
+    const std::string& float_string2) {
+  perf_gaming_info_.reset(new FloatInfo(op, float_string, float_string2));
+  return perf_gaming_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetPerfOverallInfo(
+    const std::string& op,
+    const std::string& float_string,
+    const std::string& float_string2) {
+  perf_overall_info_.reset(new FloatInfo(op, float_string, float_string2));
+  return perf_overall_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetMachineModelInfo(
+    const std::string& name_op,
+    const std::string& name_value,
+    const std::string& version_op,
+    const std::string& version_string,
+    const std::string& version_string2) {
+  machine_model_info_.reset(new MachineModelInfo(
+      name_op, name_value, version_op, version_string, version_string2));
+  return machine_model_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetGpuCountInfo(
+    const std::string& op,
+    const std::string& int_string,
+    const std::string& int_string2) {
+  gpu_count_info_.reset(new IntInfo(op, int_string, int_string2));
+  return gpu_count_info_->IsValid();
+}
+
+bool GpuControlList::GpuControlListEntry::SetFeatures(
+    const std::vector<std::string>& feature_strings,
+    const FeatureMap& feature_map,
+    bool supports_feature_type_all) {
+  size_t size = feature_strings.size();
+  if (size == 0)
+    return false;
+  features_.clear();
+  for (size_t i = 0; i < size; ++i) {
+    int feature = 0;
+    if (supports_feature_type_all && feature_strings[i] == "all") {
+      for (FeatureMap::const_iterator iter = feature_map.begin();
+           iter != feature_map.end(); ++iter)
+        features_.insert(iter->second);
+      continue;
+    }
+    if (StringToFeature(feature_strings[i], &feature, feature_map))
+      features_.insert(feature);
+    else
+      contains_unknown_features_ = true;
+  }
+  return true;
+}
+
+void GpuControlList::GpuControlListEntry::AddException(
+    ScopedGpuControlListEntry exception) {
+  exceptions_.push_back(exception);
+}
+
+// static
+GpuControlList::GpuControlListEntry::MultiGpuStyle
+GpuControlList::GpuControlListEntry::StringToMultiGpuStyle(
+    const std::string& style) {
+  if (style == kMultiGpuStyleStringOptimus)
+    return kMultiGpuStyleOptimus;
+  if (style == kMultiGpuStyleStringAMDSwitchable)
+    return kMultiGpuStyleAMDSwitchable;
+  return kMultiGpuStyleNone;
+}
+
+// static
+GpuControlList::GpuControlListEntry::MultiGpuCategory
+GpuControlList::GpuControlListEntry::StringToMultiGpuCategory(
+    const std::string& category) {
+  if (category == kMultiGpuCategoryStringPrimary)
+    return kMultiGpuCategoryPrimary;
+  if (category == kMultiGpuCategoryStringSecondary)
+    return kMultiGpuCategorySecondary;
+  if (category == kMultiGpuCategoryStringAny)
+    return kMultiGpuCategoryAny;
+  return kMultiGpuCategoryNone;
+}
+
+bool GpuControlList::GpuControlListEntry::Contains(
+    OsType os_type, const std::string& os_version,
+    const GPUInfo& gpu_info) const {
+  DCHECK(os_type != kOsAny);
+  if (os_info_.get() != NULL && !os_info_->Contains(os_type, os_version))
+    return false;
+  bool is_not_primary_gpu =
+      GpuUnmatched(vendor_id_, device_id_list_, gpu_info.gpu);
+  bool is_not_secondary_gpu = true;
+  for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) {
+    is_not_secondary_gpu = is_not_secondary_gpu &&
+        GpuUnmatched(vendor_id_, device_id_list_, gpu_info.secondary_gpus[i]);
+  }
+  switch (multi_gpu_category_) {
+    case kMultiGpuCategoryPrimary:
+      if (is_not_primary_gpu)
+        return false;
+      break;
+    case kMultiGpuCategorySecondary:
+      if (is_not_secondary_gpu)
+        return false;
+      break;
+    case kMultiGpuCategoryAny:
+      if (is_not_primary_gpu && is_not_secondary_gpu)
+        return false;
+      break;
+    case kMultiGpuCategoryNone:
+      break;
+  }
+  switch (multi_gpu_style_) {
+    case kMultiGpuStyleOptimus:
+      if (!gpu_info.optimus)
+        return false;
+      break;
+    case kMultiGpuStyleAMDSwitchable:
+      if (!gpu_info.amd_switchable)
+        return false;
+      break;
+    case kMultiGpuStyleNone:
+      break;
+  }
+  if (driver_vendor_info_.get() != NULL && !gpu_info.driver_vendor.empty() &&
+      !driver_vendor_info_->Contains(gpu_info.driver_vendor))
+    return false;
+  if (driver_version_info_.get() != NULL && !gpu_info.driver_version.empty()) {
+    if (!driver_version_info_->Contains(gpu_info.driver_version))
+      return false;
+  }
+  if (driver_date_info_.get() != NULL && !gpu_info.driver_date.empty()) {
+    if (!driver_date_info_->Contains(gpu_info.driver_date, '-'))
+      return false;
+  }
+  if (gl_vendor_info_.get() != NULL && !gpu_info.gl_vendor.empty() &&
+      !gl_vendor_info_->Contains(gpu_info.gl_vendor))
+    return false;
+  if (gl_renderer_info_.get() != NULL && !gpu_info.gl_renderer.empty() &&
+      !gl_renderer_info_->Contains(gpu_info.gl_renderer))
+    return false;
+  if (gl_extensions_info_.get() != NULL && !gpu_info.gl_extensions.empty() &&
+      !gl_extensions_info_->Contains(gpu_info.gl_extensions))
+    return false;
+  if (perf_graphics_info_.get() != NULL &&
+      (gpu_info.performance_stats.graphics == 0.0 ||
+       !perf_graphics_info_->Contains(gpu_info.performance_stats.graphics)))
+    return false;
+  if (perf_gaming_info_.get() != NULL &&
+      (gpu_info.performance_stats.gaming == 0.0 ||
+       !perf_gaming_info_->Contains(gpu_info.performance_stats.gaming)))
+    return false;
+  if (perf_overall_info_.get() != NULL &&
+      (gpu_info.performance_stats.overall == 0.0 ||
+       !perf_overall_info_->Contains(gpu_info.performance_stats.overall)))
+    return false;
+  if (machine_model_info_.get() != NULL) {
+    std::vector<std::string> name_version;
+    base::SplitString(gpu_info.machine_model, ' ', &name_version);
+    if (name_version.size() == 2 &&
+        !machine_model_info_->Contains(name_version[0], name_version[1]))
+      return false;
+  }
+  if (gpu_count_info_.get() != NULL &&
+      !gpu_count_info_->Contains(gpu_info.secondary_gpus.size() + 1))
+    return false;
+  if (cpu_brand_.get() != NULL) {
+    base::CPU cpu_info;
+    if (!cpu_brand_->Contains(cpu_info.cpu_brand()))
+      return false;
+  }
+
+  for (size_t i = 0; i < exceptions_.size(); ++i) {
+    if (exceptions_[i]->Contains(os_type, os_version, gpu_info) &&
+        !exceptions_[i]->NeedsMoreInfo(gpu_info))
+      return false;
+  }
+  return true;
+}
+
+bool GpuControlList::GpuControlListEntry::NeedsMoreInfo(
+    const GPUInfo& gpu_info) const {
+  // We only check for missing info that might be collected with a gl context.
+  // If certain info is missing due to some error, say, we fail to collect
+  // vendor_id/device_id, then even if we launch GPU process and create a gl
+  // context, we won't gather such missing info, so we still return false.
+  if (driver_vendor_info_.get() && gpu_info.driver_vendor.empty())
+    return true;
+  if (driver_version_info_.get() && gpu_info.driver_version.empty())
+    return true;
+  if (gl_vendor_info_.get() && gpu_info.gl_vendor.empty())
+    return true;
+  if (gl_renderer_info_.get() && gpu_info.gl_renderer.empty())
+    return true;
+  for (size_t i = 0; i < exceptions_.size(); ++i) {
+    if (exceptions_[i]->NeedsMoreInfo(gpu_info))
+      return true;
+  }
+  return false;
+}
+
+GpuControlList::OsType GpuControlList::GpuControlListEntry::GetOsType() const {
+  if (os_info_.get() == NULL)
+    return kOsAny;
+  return os_info_->type();
+}
+
+uint32 GpuControlList::GpuControlListEntry::id() const {
+  return id_;
+}
+
+bool GpuControlList::GpuControlListEntry::disabled() const {
+  return disabled_;
+}
+
+const std::set<int>& GpuControlList::GpuControlListEntry::features() const {
+  return features_;
+}
+
+// static
+bool GpuControlList::GpuControlListEntry::StringToFeature(
+    const std::string& feature_name, int* feature_id,
+    const FeatureMap& feature_map) {
+  FeatureMap::const_iterator iter = feature_map.find(feature_name);
+  if (iter != feature_map.end()) {
+    *feature_id = iter->second;
+    return true;
+  }
+  return false;
+}
+
+GpuControlList::GpuControlList()
+    : max_entry_id_(0),
+      contains_unknown_fields_(false),
+      needs_more_info_(false),
+      supports_feature_type_all_(false) {
+}
+
+GpuControlList::~GpuControlList() {
+  Clear();
+}
+
+bool GpuControlList::LoadList(
+    const std::string& json_context, GpuControlList::OsFilter os_filter) {
+  const std::string browser_version_string = "0";
+  return LoadList(browser_version_string, json_context, os_filter);
+}
+
+bool GpuControlList::LoadList(
+    const std::string& browser_version_string,
+    const std::string& json_context,
+    GpuControlList::OsFilter os_filter) {
+  std::vector<std::string> pieces;
+  if (!ProcessVersionString(browser_version_string, '.', &pieces))
+    return false;
+  browser_version_ = browser_version_string;
+
+  scoped_ptr<base::Value> root;
+  root.reset(base::JSONReader::Read(json_context));
+  if (root.get() == NULL || !root->IsType(base::Value::TYPE_DICTIONARY))
+    return false;
+
+  base::DictionaryValue* root_dictionary =
+      static_cast<DictionaryValue*>(root.get());
+  DCHECK(root_dictionary);
+  return LoadList(*root_dictionary, os_filter);
+}
+
+bool GpuControlList::LoadList(const base::DictionaryValue& parsed_json,
+                              GpuControlList::OsFilter os_filter) {
+  std::vector<ScopedGpuControlListEntry> entries;
+
+  parsed_json.GetString("version", &version_);
+  std::vector<std::string> pieces;
+  if (!ProcessVersionString(version_, '.', &pieces))
+    return false;
+
+  const base::ListValue* list = NULL;
+  if (!parsed_json.GetList("entries", &list))
+    return false;
+
+  uint32 max_entry_id = 0;
+  bool contains_unknown_fields = false;
+  for (size_t i = 0; i < list->GetSize(); ++i) {
+    const base::DictionaryValue* list_item = NULL;
+    bool valid = list->GetDictionary(i, &list_item);
+    if (!valid || list_item == NULL)
+      return false;
+    // Check browser version compatibility: if the entry is not for the
+    // current browser version, don't process it.
+    BrowserVersionSupport browser_version_support =
+        IsEntrySupportedByCurrentBrowserVersion(list_item);
+    if (browser_version_support == kMalformed)
+      return false;
+    if (browser_version_support == kUnsupported)
+      continue;
+    DCHECK(browser_version_support == kSupported);
+    ScopedGpuControlListEntry entry(GpuControlListEntry::GetEntryFromValue(
+        list_item, true, feature_map_, supports_feature_type_all_));
+    if (entry == NULL)
+      return false;
+    if (entry->id() > max_entry_id)
+      max_entry_id = entry->id();
+    // If an unknown field is encountered, skip the entry; if an unknown
+    // feature is encountered, ignore the feature, but keep the entry.
+    if (entry->contains_unknown_fields()) {
+      contains_unknown_fields = true;
+      continue;
+    }
+    if (entry->contains_unknown_features())
+      contains_unknown_fields = true;
+    entries.push_back(entry);
+  }
+
+  Clear();
+  OsType my_os = GetOsType();
+  for (size_t i = 0; i < entries.size(); ++i) {
+    OsType entry_os = entries[i]->GetOsType();
+    if (os_filter == GpuControlList::kAllOs ||
+        entry_os == kOsAny || entry_os == my_os)
+      entries_.push_back(entries[i]);
+  }
+  max_entry_id_ = max_entry_id;
+  contains_unknown_fields_ = contains_unknown_fields;
+  return true;
+}
+
+std::set<int> GpuControlList::MakeDecision(
+    GpuControlList::OsType os,
+    std::string os_version,
+    const GPUInfo& gpu_info) {
+  active_entries_.clear();
+  std::set<int> features;
+
+  needs_more_info_ = false;
+  std::set<int> possible_features;
+
+  if (os == kOsAny)
+    os = GetOsType();
+  if (os_version.empty()) {
+    os_version = base::SysInfo::OperatingSystemVersion();
+    size_t pos = os_version.find_first_not_of("0123456789.");
+    if (pos != std::string::npos)
+      os_version = os_version.substr(0, pos);
+  }
+  std::vector<std::string> pieces;
+  if (!ProcessVersionString(os_version, '.', &pieces))
+    os_version = "0";
+
+  for (size_t i = 0; i < entries_.size(); ++i) {
+    if (entries_[i]->Contains(os, os_version, gpu_info)) {
+      if (!entries_[i]->disabled()) {
+        MergeFeatureSets(&possible_features, entries_[i]->features());
+        if (!entries_[i]->NeedsMoreInfo(gpu_info))
+          MergeFeatureSets(&features, entries_[i]->features());
+      }
+      active_entries_.push_back(entries_[i]);
+    }
+  }
+
+  if (possible_features.size() > features.size())
+    needs_more_info_ = true;
+
+  return features;
+}
+
+void GpuControlList::GetDecisionEntries(
+    std::vector<uint32>* entry_ids, bool disabled) const {
+  DCHECK(entry_ids);
+  entry_ids->clear();
+  for (size_t i = 0; i < active_entries_.size(); ++i) {
+    if (disabled == active_entries_[i]->disabled())
+      entry_ids->push_back(active_entries_[i]->id());
+  }
+}
+
+void GpuControlList::GetReasons(base::ListValue* problem_list) const {
+  DCHECK(problem_list);
+  for (size_t i = 0; i < active_entries_.size(); ++i) {
+    GpuControlListEntry* entry = active_entries_[i];
+    if (entry->disabled())
+      continue;
+    base::DictionaryValue* problem = new base::DictionaryValue();
+
+    problem->SetString("description", entry->description());
+
+    base::ListValue* cr_bugs = new base::ListValue();
+    for (size_t j = 0; j < entry->cr_bugs().size(); ++j)
+      cr_bugs->Append(new base::FundamentalValue(entry->cr_bugs()[j]));
+    problem->Set("crBugs", cr_bugs);
+
+    base::ListValue* webkit_bugs = new base::ListValue();
+    for (size_t j = 0; j < entry->webkit_bugs().size(); ++j) {
+      webkit_bugs->Append(new base::FundamentalValue(entry->webkit_bugs()[j]));
+    }
+    problem->Set("webkitBugs", webkit_bugs);
+
+    problem_list->Append(problem);
+  }
+}
+
+size_t GpuControlList::num_entries() const {
+  return entries_.size();
+}
+
+uint32 GpuControlList::max_entry_id() const {
+  return max_entry_id_;
+}
+
+std::string GpuControlList::version() const {
+  return version_;
+}
+
+GpuControlList::OsType GpuControlList::GetOsType() {
+#if defined(OS_CHROMEOS)
+  return kOsChromeOS;
+#elif defined(OS_WIN)
+  return kOsWin;
+#elif defined(OS_ANDROID)
+  return kOsAndroid;
+#elif defined(OS_LINUX) || defined(OS_OPENBSD)
+  return kOsLinux;
+#elif defined(OS_MACOSX)
+  return kOsMacosx;
+#else
+  return kOsUnknown;
+#endif
+}
+
+void GpuControlList::Clear() {
+  entries_.clear();
+  active_entries_.clear();
+  max_entry_id_ = 0;
+  contains_unknown_fields_ = false;
+}
+
+GpuControlList::BrowserVersionSupport
+GpuControlList::IsEntrySupportedByCurrentBrowserVersion(
+    const base::DictionaryValue* value) {
+  DCHECK(value);
+  const base::DictionaryValue* browser_version_value = NULL;
+  if (value->GetDictionary("browser_version", &browser_version_value)) {
+    std::string version_op = "any";
+    std::string version_string;
+    std::string version_string2;
+    browser_version_value->GetString(kOp, &version_op);
+    browser_version_value->GetString("number", &version_string);
+    browser_version_value->GetString("number2", &version_string2);
+    scoped_ptr<VersionInfo> browser_version_info;
+    browser_version_info.reset(new VersionInfo(
+        version_op, std::string(), version_string, version_string2));
+    if (!browser_version_info->IsValid())
+      return kMalformed;
+    if (browser_version_info->Contains(browser_version_))
+      return kSupported;
+    return kUnsupported;
+  }
+  return kSupported;
+}
+
+// static
+GpuControlList::NumericOp GpuControlList::StringToNumericOp(
+    const std::string& op) {
+  if (op == "=")
+    return kEQ;
+  if (op == "<")
+    return kLT;
+  if (op == "<=")
+    return kLE;
+  if (op == ">")
+    return kGT;
+  if (op == ">=")
+    return kGE;
+  if (op == "any")
+    return kAny;
+  if (op == "between")
+    return kBetween;
+  return kUnknown;
+}
+
+void GpuControlList::AddSupportedFeature(
+    const std::string& feature_name, int feature_id) {
+  feature_map_[feature_name] = feature_id;
+}
+
+void GpuControlList::set_supports_feature_type_all(bool supported) {
+  supports_feature_type_all_ = supported;
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_control_list.h b/gpu/config/gpu_control_list.h
new file mode 100644
index 0000000..eaf7024
--- /dev/null
+++ b/gpu/config/gpu_control_list.h
@@ -0,0 +1,498 @@
+// Copyright (c) 2013 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 GPU_CONFIG_GPU_CONTROL_LIST_H_
+#define GPU_CONFIG_GPU_CONTROL_LIST_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+struct GPUInfo;
+
+class GPU_EXPORT GpuControlList {
+ public:
+  enum OsType {
+    kOsLinux,
+    kOsMacosx,
+    kOsWin,
+    kOsChromeOS,
+    kOsAndroid,
+    kOsAny,
+    kOsUnknown
+  };
+
+  enum OsFilter {
+    // In loading, ignore all entries that belong to other OS.
+    kCurrentOsOnly,
+    // In loading, keep all entries. This is for testing only.
+    kAllOs
+  };
+
+  GpuControlList();
+  virtual ~GpuControlList();
+
+  // Loads control list information from a json file.
+  // If failed, the current GpuControlList is un-touched.
+  bool LoadList(const std::string& json_context, OsFilter os_filter);
+  bool LoadList(const std::string& browser_version_string,
+                const std::string& json_context, OsFilter os_filter);
+
+  // Collects system information and combines them with gpu_info and control
+  // list information to decide which entries are applied to the current
+  // system and returns the union of features specified in each entry.
+  // If os is kOsAny, use the current OS; if os_version is empty, use the
+  // current OS version.
+  std::set<int> MakeDecision(
+      OsType os, std::string os_version, const GPUInfo& gpu_info);
+
+  // Collects the active entries from the last MakeDecision() call.
+  // If disabled set to true, return entries that are disabled; otherwise,
+  // return enabled entries.
+  void GetDecisionEntries(std::vector<uint32>* entry_ids,
+                          bool disabled) const;
+
+  // Returns the description and bugs from active entries from the last
+  // MakeDecision() call.
+  //
+  // Each problems has:
+  // {
+  //    "description": "Your GPU is too old",
+  //    "crBugs": [1234],
+  //    "webkitBugs": []
+  // }
+  void GetReasons(base::ListValue* problem_list) const;
+
+  // Return the largest entry id.  This is used for histogramming.
+  uint32 max_entry_id() const;
+
+  // Returns the version of the control list.
+  std::string version() const;
+
+  // Check if we need more gpu info to make the decisions.
+  // This is computed from the last MakeDecision() call.
+  // If yes, we should create a gl context and do a full gpu info collection.
+  bool needs_more_info() const { return needs_more_info_; }
+
+  // Check if any entries contain unknown fields.  This is only for tests.
+  bool contains_unknown_fields() const { return contains_unknown_fields_; }
+
+  // Returns the number of entries.  This is only for tests.
+  size_t num_entries() const;
+
+  // Register a feature to FeatureMap - used to construct a GpuControlList.
+  void AddSupportedFeature(const std::string& feature_name, int feature_id);
+  // Register whether "all" is recognized as all features.
+  void set_supports_feature_type_all(bool supported);
+
+ private:
+  friend class GpuControlListEntryTest;
+  friend class MachineModelInfoTest;
+  friend class NumberInfoTest;
+  friend class OsInfoTest;
+  friend class StringInfoTest;
+  friend class VersionInfoTest;
+
+  enum BrowserVersionSupport {
+    kSupported,
+    kUnsupported,
+    kMalformed
+  };
+
+  enum NumericOp {
+    kBetween,  // <= * <=
+    kEQ,  // =
+    kLT,  // <
+    kLE,  // <=
+    kGT,  // >
+    kGE,  // >=
+    kAny,
+    kUnknown  // Indicates the data is invalid.
+  };
+
+  class GPU_EXPORT VersionInfo {
+   public:
+    // If version_style is empty, it defaults to kNumerical.
+    VersionInfo(const std::string& version_op,
+                const std::string& version_style,
+                const std::string& version_string,
+                const std::string& version_string2);
+    ~VersionInfo();
+
+    // Determines if a given version is included in the VersionInfo range.
+    // "splitter" divides version string into segments.
+    bool Contains(const std::string& version, char splitter) const;
+    // Same as above, using '.' as splitter.
+    bool Contains(const std::string& version) const;
+
+    // Determine if the version_style is lexical.
+    bool IsLexical() const;
+
+    // Determines if the VersionInfo contains valid information.
+    bool IsValid() const;
+
+   private:
+    enum VersionStyle {
+      kVersionStyleNumerical,
+      kVersionStyleLexical,
+      kVersionStyleUnknown
+    };
+
+    static VersionStyle StringToVersionStyle(const std::string& version_style);
+
+    // Compare two version strings.
+    // Return 1 if version > version_ref,
+    //        0 if version = version_ref,
+    //       -1 if version < version_ref.
+    // Note that we only compare as many segments as both versions contain.
+    // For example: Compare("10.3.1", "10.3") returns 0,
+    //              Compare("10.3", "10.3.1") returns 0.
+    // If "version_style" is Lexical, the first segment is compared
+    // numerically, all other segments are compared lexically.
+    // Lexical is used for AMD Linux driver versions only.
+    static int Compare(const std::vector<std::string>& version,
+                       const std::vector<std::string>& version_ref,
+                       VersionStyle version_style);
+
+    NumericOp op_;
+    VersionStyle version_style_;
+    std::vector<std::string> version_;
+    std::vector<std::string> version2_;
+  };
+
+  class GPU_EXPORT OsInfo {
+   public:
+    OsInfo(const std::string& os,
+           const std::string& version_op,
+           const std::string& version_string,
+           const std::string& version_string2);
+    ~OsInfo();
+
+    // Determines if a given os/version is included in the OsInfo set.
+    bool Contains(OsType type, const std::string& version) const;
+
+    // Determines if the VersionInfo contains valid information.
+    bool IsValid() const;
+
+    OsType type() const;
+
+    // Maps string to OsType; returns kOsUnknown if it's not a valid os.
+    static OsType StringToOsType(const std::string& os);
+
+   private:
+    OsType type_;
+    scoped_ptr<VersionInfo> version_info_;
+  };
+
+  class GPU_EXPORT StringInfo {
+   public:
+    StringInfo(const std::string& string_op, const std::string& string_value);
+
+    // Determines if a given string is included in the StringInfo.
+    bool Contains(const std::string& value) const;
+
+    // Determines if the StringInfo contains valid information.
+    bool IsValid() const;
+
+   private:
+    enum Op {
+      kContains,
+      kBeginWith,
+      kEndWith,
+      kEQ,  // =
+      kUnknown  // Indicates StringInfo data is invalid.
+    };
+
+    // Maps string to Op; returns kUnknown if it's not a valid Op.
+    static Op StringToOp(const std::string& string_op);
+
+    Op op_;
+    std::string value_;
+  };
+
+  class GPU_EXPORT FloatInfo {
+   public:
+    FloatInfo(const std::string& float_op,
+              const std::string& float_value,
+              const std::string& float_value2);
+
+    // Determines if a given float is included in the FloatInfo.
+    bool Contains(float value) const;
+
+    // Determines if the FloatInfo contains valid information.
+    bool IsValid() const;
+
+   private:
+    NumericOp op_;
+    float value_;
+    float value2_;
+  };
+
+  class GPU_EXPORT IntInfo {
+   public:
+    IntInfo(const std::string& int_op,
+            const std::string& int_value,
+            const std::string& int_value2);
+
+    // Determines if a given int is included in the IntInfo.
+    bool Contains(int value) const;
+
+    // Determines if the IntInfo contains valid information.
+    bool IsValid() const;
+
+   private:
+    NumericOp op_;
+    int value_;
+    int value2_;
+  };
+
+  class GPU_EXPORT MachineModelInfo {
+   public:
+    MachineModelInfo(const std::string& name_op,
+                     const std::string& name_value,
+                     const std::string& version_op,
+                     const std::string& version_string,
+                     const std::string& version_string2);
+    ~MachineModelInfo();
+
+    // Determines if a given name/version is included in the MachineModelInfo.
+    bool Contains(const std::string& name, const std::string& version) const;
+
+    // Determines if the MachineModelInfo contains valid information.
+    bool IsValid() const;
+
+   private:
+    scoped_ptr<StringInfo> name_info_;
+    scoped_ptr<VersionInfo> version_info_;
+  };
+
+  class GpuControlListEntry;
+  typedef scoped_refptr<GpuControlListEntry> ScopedGpuControlListEntry;
+
+  typedef base::hash_map<std::string, int> FeatureMap;
+
+  class GPU_EXPORT GpuControlListEntry
+      : public base::RefCounted<GpuControlListEntry> {
+   public:
+    // Constructs GpuControlListEntry from DictionaryValue loaded from json.
+    // Top-level entry must have an id number.  Others are exceptions.
+    static ScopedGpuControlListEntry GetEntryFromValue(
+        const base::DictionaryValue* value, bool top_level,
+        const FeatureMap& feature_map,
+        bool supports_feature_type_all);
+
+    // Determines if a given os/gc/machine_model/driver is included in the
+    // Entry set.
+    bool Contains(OsType os_type, const std::string& os_version,
+                  const GPUInfo& gpu_info) const;
+
+    // Determines whether we needs more gpu info to make the blacklisting
+    // decision.  It should only be checked if Contains() returns true.
+    bool NeedsMoreInfo(const GPUInfo& gpu_info) const;
+
+    // Returns the OsType.
+    OsType GetOsType() const;
+
+    // Returns the entry's unique id.  0 is reserved.
+    uint32 id() const;
+
+    // Returns whether the entry is disabled.
+    bool disabled() const;
+
+    // Returns the description of the entry
+    const std::string& description() const { return description_; }
+
+    // Returns a list of Chromium and Webkit bugs applicable to this entry
+    const std::vector<int>& cr_bugs() const { return cr_bugs_; }
+    const std::vector<int>& webkit_bugs() const { return webkit_bugs_; }
+
+    // Returns the blacklisted features in this entry.
+    const std::set<int>& features() const;
+
+    // Returns true if an unknown field is encountered.
+    bool contains_unknown_fields() const {
+      return contains_unknown_fields_;
+    }
+    // Returns true if an unknown blacklist feature is encountered.
+    bool contains_unknown_features() const {
+      return contains_unknown_features_;
+    }
+
+   private:
+    friend class base::RefCounted<GpuControlListEntry>;
+
+    enum MultiGpuStyle {
+      kMultiGpuStyleOptimus,
+      kMultiGpuStyleAMDSwitchable,
+      kMultiGpuStyleNone
+    };
+
+    enum MultiGpuCategory {
+      kMultiGpuCategoryPrimary,
+      kMultiGpuCategorySecondary,
+      kMultiGpuCategoryAny,
+      kMultiGpuCategoryNone
+    };
+
+    GpuControlListEntry();
+    ~GpuControlListEntry();
+
+    bool SetId(uint32 id);
+
+    void SetDisabled(bool disabled);
+
+    bool SetOsInfo(const std::string& os,
+                   const std::string& version_op,
+                   const std::string& version_string,
+                   const std::string& version_string2);
+
+    bool SetVendorId(const std::string& vendor_id_string);
+
+    bool AddDeviceId(const std::string& device_id_string);
+
+    bool SetMultiGpuStyle(const std::string& multi_gpu_style_string);
+
+    bool SetMultiGpuCategory(const std::string& multi_gpu_category_string);
+
+    bool SetDriverVendorInfo(const std::string& vendor_op,
+                             const std::string& vendor_value);
+
+    bool SetDriverVersionInfo(const std::string& version_op,
+                              const std::string& version_style,
+                              const std::string& version_string,
+                              const std::string& version_string2);
+
+    bool SetDriverDateInfo(const std::string& date_op,
+                           const std::string& date_string,
+                           const std::string& date_string2);
+
+    bool SetGLVendorInfo(const std::string& vendor_op,
+                         const std::string& vendor_value);
+
+    bool SetGLRendererInfo(const std::string& renderer_op,
+                           const std::string& renderer_value);
+
+    bool SetGLExtensionsInfo(const std::string& extensions_op,
+                             const std::string& extensions_value);
+
+    bool SetCpuBrand(const std::string& cpu_op,
+                     const std::string& cpu_value);
+
+    bool SetPerfGraphicsInfo(const std::string& op,
+                             const std::string& float_string,
+                             const std::string& float_string2);
+
+    bool SetPerfGamingInfo(const std::string& op,
+                           const std::string& float_string,
+                           const std::string& float_string2);
+
+    bool SetPerfOverallInfo(const std::string& op,
+                            const std::string& float_string,
+                            const std::string& float_string2);
+
+    bool SetMachineModelInfo(const std::string& name_op,
+                             const std::string& name_value,
+                             const std::string& version_op,
+                             const std::string& version_string,
+                             const std::string& version_string2);
+
+    bool SetGpuCountInfo(const std::string& op,
+                         const std::string& int_string,
+                         const std::string& int_string2);
+
+    bool SetFeatures(const std::vector<std::string>& features,
+                     const FeatureMap& feature_map,
+                     bool supports_feature_type_all);
+
+    void AddException(ScopedGpuControlListEntry exception);
+
+    static MultiGpuStyle StringToMultiGpuStyle(const std::string& style);
+
+    static MultiGpuCategory StringToMultiGpuCategory(
+        const std::string& category);
+
+    // map a feature_name to feature_id. If the string is not a registered
+    // feature name, return false.
+    static bool StringToFeature(const std::string& feature_name,
+                                int* feature_id,
+                                const FeatureMap& feature_map);
+
+    uint32 id_;
+    bool disabled_;
+    std::string description_;
+    std::vector<int> cr_bugs_;
+    std::vector<int> webkit_bugs_;
+    scoped_ptr<OsInfo> os_info_;
+    uint32 vendor_id_;
+    std::vector<uint32> device_id_list_;
+    MultiGpuStyle multi_gpu_style_;
+    MultiGpuCategory multi_gpu_category_;
+    scoped_ptr<StringInfo> driver_vendor_info_;
+    scoped_ptr<VersionInfo> driver_version_info_;
+    scoped_ptr<VersionInfo> driver_date_info_;
+    scoped_ptr<StringInfo> gl_vendor_info_;
+    scoped_ptr<StringInfo> gl_renderer_info_;
+    scoped_ptr<StringInfo> gl_extensions_info_;
+    scoped_ptr<StringInfo> cpu_brand_;
+    scoped_ptr<FloatInfo> perf_graphics_info_;
+    scoped_ptr<FloatInfo> perf_gaming_info_;
+    scoped_ptr<FloatInfo> perf_overall_info_;
+    scoped_ptr<MachineModelInfo> machine_model_info_;
+    scoped_ptr<IntInfo> gpu_count_info_;
+    std::set<int> features_;
+    std::vector<ScopedGpuControlListEntry> exceptions_;
+    bool contains_unknown_fields_;
+    bool contains_unknown_features_;
+  };
+
+  // Gets the current OS type.
+  static OsType GetOsType();
+
+  bool LoadList(const base::DictionaryValue& parsed_json, OsFilter os_filter);
+
+  void Clear();
+
+  // Check if the entry is supported by the current version of browser.
+  // By default, if there is no browser version information in the entry,
+  // return kSupported;
+  BrowserVersionSupport IsEntrySupportedByCurrentBrowserVersion(
+      const base::DictionaryValue* value);
+
+  static NumericOp StringToNumericOp(const std::string& op);
+
+  std::string version_;
+  std::vector<ScopedGpuControlListEntry> entries_;
+
+  std::string browser_version_;
+
+  // This records all the blacklist entries that are appliable to the current
+  // user machine.  It is updated everytime MakeDecision() is called and is
+  // used later by GetDecisionEntries().
+  std::vector<ScopedGpuControlListEntry> active_entries_;
+
+  uint32 max_entry_id_;
+
+  bool contains_unknown_fields_;
+
+  bool needs_more_info_;
+
+  // The features a GpuControlList recognizes and handles.
+  FeatureMap feature_map_;
+  bool supports_feature_type_all_;
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_CONTROL_LIST_H_
+
diff --git a/gpu/config/gpu_control_list_entry_unittest.cc b/gpu/config/gpu_control_list_entry_unittest.cc
new file mode 100644
index 0000000..b4505bc
--- /dev/null
+++ b/gpu/config/gpu_control_list_entry_unittest.cc
@@ -0,0 +1,748 @@
+// Copyright (c) 2013 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/json/json_reader.h"
+#include "gpu/config/gpu_control_list.h"
+#include "gpu/config/gpu_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define LONG_STRING_CONST(...) #__VA_ARGS__
+
+namespace gpu {
+
+enum TestFeatureType {
+  TEST_FEATURE_0 = 0,
+  TEST_FEATURE_1,
+  TEST_FEATURE_2
+};
+
+class GpuControlListEntryTest : public testing::Test {
+ public:
+  GpuControlListEntryTest() { }
+  virtual ~GpuControlListEntryTest() { }
+
+  const GPUInfo& gpu_info() const {
+    return gpu_info_;
+  }
+
+  typedef GpuControlList::ScopedGpuControlListEntry ScopedEntry;
+
+  static ScopedEntry GetEntryFromString(
+      const std::string& json, bool supports_feature_type_all) {
+    scoped_ptr<base::Value> root;
+    root.reset(base::JSONReader::Read(json));
+    DictionaryValue* value = NULL;
+    if (root.get() == NULL || !root->GetAsDictionary(&value))
+      return NULL;
+
+    GpuControlList::FeatureMap feature_map;
+    feature_map["test_feature_0"] = TEST_FEATURE_0;
+    feature_map["test_feature_1"] = TEST_FEATURE_1;
+    feature_map["test_feature_2"] = TEST_FEATURE_2;
+
+    return GpuControlList::GpuControlListEntry::GetEntryFromValue(
+        value, true, feature_map, supports_feature_type_all);
+  }
+
+  static ScopedEntry GetEntryFromString(const std::string& json) {
+    return GetEntryFromString(json, false);
+  }
+
+  virtual void SetUp() {
+    gpu_info_.gpu.vendor_id = 0x10de;
+    gpu_info_.gpu.device_id = 0x0640;
+    gpu_info_.driver_vendor = "NVIDIA";
+    gpu_info_.driver_version = "1.6.18";
+    gpu_info_.driver_date = "7-14-2009";
+    gpu_info_.machine_model = "MacBookPro 7.1";
+    gpu_info_.gl_vendor = "NVIDIA Corporation";
+    gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
+    gpu_info_.performance_stats.graphics = 5.0;
+    gpu_info_.performance_stats.gaming = 5.0;
+    gpu_info_.performance_stats.overall = 5.0;
+  }
+
+ private:
+  GPUInfo gpu_info_;
+};
+
+TEST_F(GpuControlListEntryTest, DetailedEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 5,
+        "description": "test entry",
+        "cr_bugs": [1024, 678],
+        "webkit_bugs": [1950],
+        "os": {
+          "type": "macosx",
+          "version": {
+            "op": "=",
+            "number": "10.6.4"
+          }
+        },
+        "vendor_id": "0x10de",
+        "device_id": ["0x0640"],
+        "driver_version": {
+          "op": "=",
+          "number": "1.6.18"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsMacosx, entry->GetOsType());
+  EXPECT_FALSE(entry->disabled());
+  EXPECT_EQ(5u, entry->id());
+  EXPECT_STREQ("test entry", entry->description().c_str());
+  EXPECT_EQ(2u, entry->cr_bugs().size());
+  EXPECT_EQ(1024, entry->cr_bugs()[0]);
+  EXPECT_EQ(678, entry->cr_bugs()[1]);
+  EXPECT_EQ(1u, entry->webkit_bugs().size());
+  EXPECT_EQ(1950, entry->webkit_bugs()[0]);
+  EXPECT_EQ(1u, entry->features().size());
+  EXPECT_EQ(1u, entry->features().count(TEST_FEATURE_0));
+  EXPECT_FALSE(entry->contains_unknown_fields());
+  EXPECT_FALSE(entry->contains_unknown_features());
+  EXPECT_FALSE(entry->NeedsMoreInfo(gpu_info()));
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsMacosx, "10.6.4", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, VendorOnAllOsEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "vendor_id": "0x10de",
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsAny, entry->GetOsType());
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_TRUE(entry->Contains(os_type[i], "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, VendorOnLinuxEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "linux"
+        },
+        "vendor_id": "0x10de",
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsLinux, entry->GetOsType());
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_FALSE(entry->Contains(os_type[i], "10.6", gpu_info()));
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsLinux, "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, AllExceptNVidiaOnLinuxEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "linux"
+        },
+        "exceptions": [
+          {
+            "vendor_id": "0x10de"
+          }
+        ],
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsLinux, entry->GetOsType());
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_FALSE(entry->Contains(os_type[i], "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, AllExceptIntelOnLinuxEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "linux"
+        },
+        "exceptions": [
+          {
+            "vendor_id": "0x8086"
+          }
+        ],
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsLinux, entry->GetOsType());
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_FALSE(entry->Contains(os_type[i], "10.6", gpu_info()));
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsLinux, "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, DateOnWindowsEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "win"
+        },
+        "driver_date": {
+          "op": "<",
+          "number": "2010.5.8"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsWin, entry->GetOsType());
+
+  GPUInfo gpu_info;
+  gpu_info.driver_date = "4-12-2010";
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsWin, "10.6", gpu_info));
+  gpu_info.driver_date = "5-8-2010";
+  EXPECT_FALSE(entry->Contains(
+      GpuControlList::kOsWin, "10.6", gpu_info));
+  gpu_info.driver_date = "5-9-2010";
+  EXPECT_FALSE(entry->Contains(
+      GpuControlList::kOsWin, "10.6", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, MultipleDevicesEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "vendor_id": "0x10de",
+        "device_id": ["0x1023", "0x0640"],
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsAny, entry->GetOsType());
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_TRUE(entry->Contains(os_type[i], "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, ChromeOSEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "chromeos"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsChromeOS, entry->GetOsType());
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_FALSE(entry->Contains(os_type[i], "10.6", gpu_info()));
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsChromeOS, "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, MalformedVendor) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "vendor_id": "[0x10de]",
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry == NULL);
+}
+
+TEST_F(GpuControlListEntryTest, UnknownFieldEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "unknown_field": 0,
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_TRUE(entry->contains_unknown_fields());
+  EXPECT_FALSE(entry->contains_unknown_features());
+}
+
+TEST_F(GpuControlListEntryTest, UnknownExceptionFieldEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 2,
+        "exceptions": [
+          {
+            "unknown_field": 0
+          }
+        ],
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_TRUE(entry->contains_unknown_fields());
+  EXPECT_FALSE(entry->contains_unknown_features());
+}
+
+TEST_F(GpuControlListEntryTest, UnknownFeatureEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "features": [
+          "some_unknown_feature",
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_FALSE(entry->contains_unknown_fields());
+  EXPECT_TRUE(entry->contains_unknown_features());
+  EXPECT_EQ(1u, entry->features().size());
+  EXPECT_EQ(1u, entry->features().count(TEST_FEATURE_0));
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_TRUE(entry->Contains(os_type[i], "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, GlVendorEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "gl_vendor": {
+          "op": "beginwith",
+          "value": "NVIDIA"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_TRUE(entry->Contains(os_type[i], "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, GlRendererEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "gl_renderer": {
+          "op": "contains",
+          "value": "GeForce"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid
+  };
+  for (size_t i = 0; i < arraysize(os_type); ++i)
+    EXPECT_TRUE(entry->Contains(os_type[i], "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, PerfGraphicsEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "perf_graphics": {
+          "op": "<",
+          "value": "6.0"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsWin, "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, PerfGamingEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "perf_graphics": {
+          "op": "<=",
+          "value": "4.0"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_FALSE(entry->Contains(
+      GpuControlList::kOsWin, "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, PerfOverallEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "perf_overall": {
+          "op": "between",
+          "value": "1.0",
+          "value2": "9.0"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsWin, "10.6", gpu_info()));
+}
+
+TEST_F(GpuControlListEntryTest, DisabledEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "disabled": true,
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_TRUE(entry->disabled());
+}
+
+TEST_F(GpuControlListEntryTest, OptimusEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "linux"
+        },
+        "multi_gpu_style": "optimus",
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  GPUInfo gpu_info;
+  gpu_info.optimus = true;
+
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsLinux, entry->GetOsType());
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsLinux, "10.6", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, AMDSwitchableEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "macosx"
+        },
+        "multi_gpu_style": "amd_switchable",
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  GPUInfo gpu_info;
+  gpu_info.amd_switchable = true;
+
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsMacosx, entry->GetOsType());
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsMacosx, "10.6", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, LexicalDriverVersionEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "linux"
+        },
+        "vendor_id": "0x1002",
+        "driver_version": {
+          "op": "=",
+          "style": "lexical",
+          "number": "8.76"
+        },
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x1002;
+
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsLinux, entry->GetOsType());
+
+  gpu_info.driver_version = "8.76";
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsLinux, "10.6", gpu_info));
+
+  gpu_info.driver_version = "8.768";
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsLinux, "10.6", gpu_info));
+
+  gpu_info.driver_version = "8.76.8";
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsLinux, "10.6", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, MultipleGPUsAnyEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "macosx"
+        },
+        "vendor_id": "0x8086",
+        "device_id": ["0x0166"],
+        "multi_gpu_category": "any",
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsMacosx, entry->GetOsType());
+
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x10de;
+  gpu_info.gpu.device_id = 0x1976;
+  EXPECT_FALSE(entry->Contains(
+      GpuControlList::kOsMacosx, "10.6", gpu_info));
+
+  GPUInfo::GPUDevice gpu_device;
+  gpu_device.vendor_id = 0x8086;
+  gpu_device.device_id = 0x0166;
+  gpu_info.secondary_gpus.push_back(gpu_device);
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsMacosx, "10.6", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, MultipleGPUsSecondaryEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "os": {
+          "type": "macosx"
+        },
+        "vendor_id": "0x8086",
+        "device_id": ["0x0166"],
+        "multi_gpu_category": "secondary",
+        "features": [
+          "test_feature_0"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(GpuControlList::kOsMacosx, entry->GetOsType());
+
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x10de;
+  gpu_info.gpu.device_id = 0x1976;
+  EXPECT_FALSE(entry->Contains(
+      GpuControlList::kOsMacosx, "10.6", gpu_info));
+
+  GPUInfo::GPUDevice gpu_device;
+  gpu_device.vendor_id = 0x8086;
+  gpu_device.device_id = 0x0166;
+  gpu_info.secondary_gpus.push_back(gpu_device);
+  EXPECT_TRUE(entry->Contains(
+      GpuControlList::kOsMacosx, "10.6", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, NeedsMoreInfoEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "vendor_id": "0x8086",
+        "driver_version": {
+          "op": "<",
+          "number": "10.7"
+        },
+        "features": [
+          "test_feature_1"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x8086;
+  EXPECT_TRUE(entry->NeedsMoreInfo(gpu_info));
+
+  gpu_info.driver_version = "10.6";
+  EXPECT_FALSE(entry->NeedsMoreInfo(gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, NeedsMoreInfoForExceptionsEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "vendor_id": "0x8086",
+        "exceptions": [
+          {
+            "gl_renderer": {
+              "op": "contains",
+              "value": "mesa"
+            }
+          }
+        ],
+        "features": [
+          "test_feature_1"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json));
+  EXPECT_TRUE(entry != NULL);
+
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x8086;
+  EXPECT_TRUE(entry->NeedsMoreInfo(gpu_info));
+
+  gpu_info.gl_renderer = "mesa";
+  EXPECT_FALSE(entry->NeedsMoreInfo(gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, FeatureTypeAllEntry) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "id": 1,
+        "features": [
+          "all"
+        ]
+      }
+  );
+  ScopedEntry entry(GetEntryFromString(json, true));
+  EXPECT_TRUE(entry != NULL);
+  EXPECT_EQ(3u, entry->features().size());
+  EXPECT_EQ(1u, entry->features().count(TEST_FEATURE_0));
+  EXPECT_EQ(1u, entry->features().count(TEST_FEATURE_1));
+  EXPECT_EQ(1u, entry->features().count(TEST_FEATURE_2));
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_control_list_jsons.h b/gpu/config/gpu_control_list_jsons.h
new file mode 100644
index 0000000..671aa9e
--- /dev/null
+++ b/gpu/config/gpu_control_list_jsons.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2013 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 GPU_CONFIG_GPU_CONTROL_LIST_JSONS_H_
+#define GPU_CONFIG_GPU_CONTROL_LIST_JSONS_H_
+
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+GPU_EXPORT extern const char kGpuDriverBugListJson[];
+GPU_EXPORT extern const char kGpuSwitchingListJson[];
+GPU_EXPORT extern const char kSoftwareRenderingListJson[];
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_CONTROL_LIST_JSONS_H_
+
diff --git a/gpu/config/gpu_control_list_machine_model_info_unittest.cc b/gpu/config/gpu_control_list_machine_model_info_unittest.cc
new file mode 100644
index 0000000..9c681a9
--- /dev/null
+++ b/gpu/config/gpu_control_list_machine_model_info_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_control_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class MachineModelInfoTest : public testing::Test {
+ public:
+  MachineModelInfoTest() { }
+  virtual ~MachineModelInfoTest() { }
+
+  typedef GpuControlList::MachineModelInfo MachineModelInfo;
+};
+
+TEST_F(MachineModelInfoTest, ValidModelInfo) {
+  const std::string name_op[] = {
+    "contains",
+    "beginwith",
+    "endwith",
+    "="
+  };
+  const std::string version_op[] = {
+    "=",
+    "<",
+    "<=",
+    ">",
+    ">=",
+    "any",
+    "between"
+  };
+  for (size_t i = 0; i < arraysize(name_op); ++i) {
+    for (size_t j = 0; j < arraysize(version_op); ++j) {
+      std::string version1;
+      std::string version2;
+      if (version_op[j] != "any")
+        version1 = "3.14";
+      if (version_op[j] == "between")
+        version2 = "5.4";
+      MachineModelInfo info(name_op[i], "model",
+                            version_op[j], version1, version2);
+      EXPECT_TRUE(info.IsValid());
+    }
+  }
+}
+
+TEST_F(MachineModelInfoTest, ModelComparison) {
+  MachineModelInfo info("=", "model_a", ">", "3.4", std::string());
+  EXPECT_TRUE(info.Contains("model_a", "4"));
+  EXPECT_FALSE(info.Contains("model_b", "4"));
+  EXPECT_FALSE(info.Contains("model_a", "3.2"));
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_control_list_number_info_unittest.cc b/gpu/config/gpu_control_list_number_info_unittest.cc
new file mode 100644
index 0000000..5bd732c
--- /dev/null
+++ b/gpu/config/gpu_control_list_number_info_unittest.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_control_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class NumberInfoTest : public testing::Test {
+ public:
+  NumberInfoTest() { }
+  virtual ~NumberInfoTest() { }
+
+  typedef GpuControlList::FloatInfo FloatInfo;
+  typedef GpuControlList::IntInfo IntInfo;
+};
+
+TEST_F(NumberInfoTest, ValidFloatInfo) {
+  const std::string op[] = {
+    "=",
+    "<",
+    "<=",
+    ">",
+    ">=",
+    "any",
+    "between"
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    std::string value1;
+    std::string value2;
+    if (op[i] != "any")
+      value1 = "3.14";
+    if (op[i] == "between")
+      value2 = "4.21";
+    FloatInfo info(op[i], value1, value2);
+    EXPECT_TRUE(info.IsValid());
+  }
+
+  const std::string value[] = {
+    "1.0E12",
+    "1.0e12",
+    "2013",
+    "1.0e-12",
+    "2.1400",
+    "-2.14",
+  };
+  for (size_t i = 0; i < arraysize(value); ++i) {
+    FloatInfo info("=", value[i], std::string());
+    EXPECT_TRUE(info.IsValid());
+  }
+}
+
+TEST_F(NumberInfoTest, InvalidFloatInfo) {
+  const std::string op[] = {
+    "=",
+    "<",
+    "<=",
+    ">",
+    ">=",
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    FloatInfo info(op[i], std::string(), std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+  {
+    FloatInfo info("between", "3.14", std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+  const std::string value[] = {
+    "1.0 E12",
+    "1.0e 12",
+    " 2013",
+    "2013 ",
+    "- 2.14",
+  };
+  for (size_t i = 0; i < arraysize(value); ++i) {
+    FloatInfo info("=", value[i], std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+}
+
+TEST_F(NumberInfoTest, FloatComparison) {
+  {
+    FloatInfo info("=", "3.14", std::string());
+    EXPECT_TRUE(info.Contains(3.14f));
+    EXPECT_TRUE(info.Contains(3.1400f));
+    EXPECT_FALSE(info.Contains(3.1f));
+    EXPECT_FALSE(info.Contains(3));
+  }
+  {
+    FloatInfo info(">", "3.14", std::string());
+    EXPECT_FALSE(info.Contains(3.14f));
+    EXPECT_TRUE(info.Contains(3.141f));
+    EXPECT_FALSE(info.Contains(3.1f));
+  }
+  {
+    FloatInfo info("<=", "3.14", std::string());
+    EXPECT_TRUE(info.Contains(3.14f));
+    EXPECT_FALSE(info.Contains(3.141f));
+    EXPECT_TRUE(info.Contains(3.1f));
+  }
+  {
+    FloatInfo info("any", std::string(), std::string());
+    EXPECT_TRUE(info.Contains(3.14f));
+  }
+  {
+    FloatInfo info("between", "3.14", "5.4");
+    EXPECT_TRUE(info.Contains(3.14f));
+    EXPECT_TRUE(info.Contains(5.4f));
+    EXPECT_TRUE(info.Contains(4));
+    EXPECT_FALSE(info.Contains(5.6f));
+    EXPECT_FALSE(info.Contains(3.12f));
+  }
+}
+
+TEST_F(NumberInfoTest, ValidIntInfo) {
+  const std::string op[] = {
+    "=",
+    "<",
+    "<=",
+    ">",
+    ">=",
+    "any",
+    "between"
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    std::string value1;
+    std::string value2;
+    if (op[i] != "any")
+      value1 = "3";
+    if (op[i] == "between")
+      value2 = "9";
+    IntInfo info(op[i], value1, value2);
+    EXPECT_TRUE(info.IsValid());
+  }
+
+  const std::string value[] = {
+    "12",
+    "-12",
+  };
+  for (size_t i = 0; i < arraysize(value); ++i) {
+    IntInfo info("=", value[i], std::string());
+    EXPECT_TRUE(info.IsValid());
+  }
+}
+
+TEST_F(NumberInfoTest, InvalidIntInfo) {
+  const std::string op[] = {
+    "=",
+    "<",
+    "<=",
+    ">",
+    ">=",
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    IntInfo info(op[i], std::string(), std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+  {
+    IntInfo info("between", "3", std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+  const std::string value[] = {
+    " 12",
+    "12 ",
+    "- 12",
+    " -12",
+    "3.14"
+  };
+  for (size_t i = 0; i < arraysize(value); ++i) {
+    IntInfo info("=", value[i], std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+}
+
+TEST_F(NumberInfoTest, IntComparison) {
+  {
+    IntInfo info("=", "3", std::string());
+    EXPECT_TRUE(info.Contains(3));
+    EXPECT_FALSE(info.Contains(4));
+  }
+  {
+    IntInfo info(">", "3", std::string());
+    EXPECT_FALSE(info.Contains(2));
+    EXPECT_FALSE(info.Contains(3));
+    EXPECT_TRUE(info.Contains(4));
+  }
+  {
+    IntInfo info("<=", "3", std::string());
+    EXPECT_TRUE(info.Contains(2));
+    EXPECT_TRUE(info.Contains(3));
+    EXPECT_FALSE(info.Contains(4));
+  }
+  {
+    IntInfo info("any", std::string(), std::string());
+    EXPECT_TRUE(info.Contains(3));
+  }
+  {
+    IntInfo info("between", "3", "5");
+    EXPECT_TRUE(info.Contains(3));
+    EXPECT_TRUE(info.Contains(5));
+    EXPECT_TRUE(info.Contains(4));
+    EXPECT_FALSE(info.Contains(6));
+    EXPECT_FALSE(info.Contains(2));
+  }
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_control_list_os_info_unittest.cc b/gpu/config/gpu_control_list_os_info_unittest.cc
new file mode 100644
index 0000000..851f991
--- /dev/null
+++ b/gpu/config/gpu_control_list_os_info_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_control_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class OsInfoTest : public testing::Test {
+ public:
+  OsInfoTest() { }
+  virtual ~OsInfoTest() { }
+
+  typedef GpuControlList::OsInfo OsInfo;
+};
+
+TEST_F(OsInfoTest, ValidOsInfo) {
+  const std::string os[] = {
+    "win",
+    "linux",
+    "macosx",
+    "chromeos",
+    "android",
+    "any"
+  };
+  const GpuControlList::OsType os_type[] = {
+    GpuControlList::kOsWin,
+    GpuControlList::kOsLinux,
+    GpuControlList::kOsMacosx,
+    GpuControlList::kOsChromeOS,
+    GpuControlList::kOsAndroid,
+    GpuControlList::kOsAny
+  };
+  for (size_t i = 0; i < arraysize(os); ++i) {
+    OsInfo info(os[i], "=", "10.6", std::string());
+    EXPECT_TRUE(info.IsValid());
+    EXPECT_EQ(os_type[i], info.type());
+  }
+  {
+    OsInfo info("any", "any", std::string(), std::string());
+    EXPECT_TRUE(info.IsValid());
+  }
+}
+
+TEST_F(OsInfoTest, InvalidOsInfo) {
+  const std::string os[] = {
+    "win",
+    "linux",
+    "macosx",
+    "chromeos",
+    "android",
+    "any"
+  };
+  for (size_t i = 0; i < arraysize(os); ++i) {
+    {
+      OsInfo info(os[i], std::string(), std::string(), std::string());
+      EXPECT_FALSE(info.IsValid());
+    }
+    {
+      OsInfo info(os[i], "=", std::string(), std::string());
+      EXPECT_FALSE(info.IsValid());
+    }
+    {
+      OsInfo info(os[i], std::string(), "10.6", std::string());
+      EXPECT_FALSE(info.IsValid());
+    }
+  }
+  const std::string os_cap[] = {
+    "Win",
+    "Linux",
+    "MacOSX",
+    "ChromeOS",
+    "Android",
+  };
+  for (size_t i = 0; i < arraysize(os_cap); ++i) {
+    OsInfo info(os_cap[i], "=", "10.6", std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+}
+
+TEST_F(OsInfoTest, OsComparison) {
+  {
+    OsInfo info("any", "any", std::string(), std::string());
+    const GpuControlList::OsType os_type[] = {
+      GpuControlList::kOsWin, GpuControlList::kOsLinux,
+      GpuControlList::kOsMacosx, GpuControlList::kOsChromeOS,
+      GpuControlList::kOsAndroid,
+    };
+    for (size_t i = 0; i < arraysize(os_type); ++i) {
+      EXPECT_TRUE(info.Contains(os_type[i], std::string()));
+      EXPECT_TRUE(info.Contains(os_type[i], "7.8"));
+    }
+  }
+  {
+    OsInfo info("win", ">=", "6", std::string());
+    EXPECT_FALSE(info.Contains(GpuControlList::kOsMacosx, "10.8.3"));
+    EXPECT_FALSE(info.Contains(GpuControlList::kOsLinux, "10"));
+    EXPECT_FALSE(info.Contains(GpuControlList::kOsChromeOS, "13"));
+    EXPECT_FALSE(info.Contains(GpuControlList::kOsAndroid, "7"));
+    EXPECT_FALSE(info.Contains(GpuControlList::kOsAny, "7"));
+    EXPECT_FALSE(info.Contains(GpuControlList::kOsWin, std::string()));
+    EXPECT_TRUE(info.Contains(GpuControlList::kOsWin, "6"));
+    EXPECT_TRUE(info.Contains(GpuControlList::kOsWin, "6.1"));
+    EXPECT_TRUE(info.Contains(GpuControlList::kOsWin, "7"));
+    EXPECT_FALSE(info.Contains(GpuControlList::kOsWin, "5"));
+  }
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_control_list_string_info_unittest.cc b/gpu/config/gpu_control_list_string_info_unittest.cc
new file mode 100644
index 0000000..39e2f58
--- /dev/null
+++ b/gpu/config/gpu_control_list_string_info_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_control_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class StringInfoTest : public testing::Test {
+ public:
+  StringInfoTest() { }
+  virtual ~StringInfoTest() { }
+
+  typedef GpuControlList::StringInfo StringInfo;
+};
+
+TEST_F(StringInfoTest, ValidStringInfo) {
+  const std::string op[] = {
+    "contains",
+    "beginwith",
+    "endwith",
+    "="
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    {
+      StringInfo info(op[i], std::string());
+      EXPECT_TRUE(info.IsValid());
+    }
+    {
+      StringInfo info(op[i], "hello");
+      EXPECT_TRUE(info.IsValid());
+    }
+  }
+}
+
+TEST_F(StringInfoTest, InvalidStringInfo) {
+  const std::string op[] = {
+    "Contains",
+    "BeginWith",
+    "EndWith",
+    " =",
+    "= "
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    StringInfo info(op[i], "hello");
+    EXPECT_FALSE(info.IsValid());
+  }
+}
+
+TEST_F(StringInfoTest, StringComparison) {
+  {
+    StringInfo info("contains", "happy");
+    EXPECT_TRUE(info.Contains("unhappy"));
+    EXPECT_TRUE(info.Contains("happy1"));
+    EXPECT_TRUE(info.Contains("happy"));
+    EXPECT_TRUE(info.Contains("a happy dog"));
+    EXPECT_TRUE(info.Contains("Happy"));
+    EXPECT_TRUE(info.Contains("HAPPY"));
+    EXPECT_FALSE(info.Contains("ha-ppy"));
+  }
+  {
+    StringInfo info("beginwith", "happy");
+    EXPECT_FALSE(info.Contains("unhappy"));
+    EXPECT_TRUE(info.Contains("happy1"));
+    EXPECT_TRUE(info.Contains("happy"));
+    EXPECT_FALSE(info.Contains("a happy dog"));
+    EXPECT_TRUE(info.Contains("Happy"));
+    EXPECT_TRUE(info.Contains("HAPPY"));
+    EXPECT_FALSE(info.Contains("ha-ppy"));
+  }
+  {
+    StringInfo info("endwith", "happy");
+    EXPECT_TRUE(info.Contains("unhappy"));
+    EXPECT_FALSE(info.Contains("happy1"));
+    EXPECT_TRUE(info.Contains("happy"));
+    EXPECT_FALSE(info.Contains("a happy dog"));
+    EXPECT_TRUE(info.Contains("Happy"));
+    EXPECT_TRUE(info.Contains("HAPPY"));
+    EXPECT_FALSE(info.Contains("ha-ppy"));
+  }
+  {
+    StringInfo info("=", "happy");
+    EXPECT_FALSE(info.Contains("unhappy"));
+    EXPECT_FALSE(info.Contains("happy1"));
+    EXPECT_TRUE(info.Contains("happy"));
+    EXPECT_FALSE(info.Contains("a happy dog"));
+    EXPECT_TRUE(info.Contains("Happy"));
+    EXPECT_TRUE(info.Contains("HAPPY"));
+    EXPECT_FALSE(info.Contains("ha-ppy"));
+    EXPECT_FALSE(info.Contains("ha ppy"));
+    EXPECT_FALSE(info.Contains(" happy"));
+    EXPECT_FALSE(info.Contains("happy "));
+  }
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_control_list_unittest.cc b/gpu/config/gpu_control_list_unittest.cc
new file mode 100644
index 0000000..447b684
--- /dev/null
+++ b/gpu/config/gpu_control_list_unittest.cc
@@ -0,0 +1,444 @@
+// Copyright (c) 2013 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/scoped_ptr.h"
+#include "gpu/config/gpu_control_list.h"
+#include "gpu/config/gpu_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const char kOsVersion[] = "10.6.4";
+const uint32 kIntelVendorId = 0x8086;
+const uint32 kIntelDeviceId = 0x0166;  // 3rd Gen Core Graphics
+const uint32 kNvidiaVendorId = 0x10de;
+const uint32 kNvidiaDeviceId = 0x0fd5;  // GeForce GT 650M
+
+#define LONG_STRING_CONST(...) #__VA_ARGS__
+
+#define EXPECT_EMPTY_SET(feature_set) EXPECT_EQ(0u, feature_set.size())
+#define EXPECT_SINGLE_FEATURE(feature_set, feature) \
+    EXPECT_TRUE(feature_set.size() == 1 && feature_set.count(feature) == 1)
+
+namespace gpu {
+
+enum TestFeatureType {
+  TEST_FEATURE_0 = 1,
+  TEST_FEATURE_1 = 1 << 2,
+  TEST_FEATURE_2 = 1 << 3,
+};
+
+class GpuControlListTest : public testing::Test {
+ public:
+  GpuControlListTest() { }
+
+  virtual ~GpuControlListTest() { }
+
+  const GPUInfo& gpu_info() const {
+    return gpu_info_;
+  }
+
+  GpuControlList* Create() {
+    GpuControlList* rt = new GpuControlList();
+    rt->AddSupportedFeature("test_feature_0", TEST_FEATURE_0);
+    rt->AddSupportedFeature("test_feature_1", TEST_FEATURE_1);
+    rt->AddSupportedFeature("test_feature_2", TEST_FEATURE_2);
+    return rt;
+  }
+
+ protected:
+  virtual void SetUp() {
+    gpu_info_.gpu.vendor_id = kNvidiaVendorId;
+    gpu_info_.gpu.device_id = 0x0640;
+    gpu_info_.driver_vendor = "NVIDIA";
+    gpu_info_.driver_version = "1.6.18";
+    gpu_info_.driver_date = "7-14-2009";
+    gpu_info_.machine_model = "MacBookPro 7.1";
+    gpu_info_.gl_vendor = "NVIDIA Corporation";
+    gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
+    gpu_info_.performance_stats.graphics = 5.0;
+    gpu_info_.performance_stats.gaming = 5.0;
+    gpu_info_.performance_stats.overall = 5.0;
+  }
+
+  virtual void TearDown() {
+  }
+
+ private:
+  GPUInfo gpu_info_;
+};
+
+TEST_F(GpuControlListTest, DefaultControlListSettings) {
+  scoped_ptr<GpuControlList> control_list(Create());
+  // Default control list settings: all feature are allowed.
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
+  EXPECT_EMPTY_SET(features);
+}
+
+TEST_F(GpuControlListTest, EmptyControlList) {
+  // Empty list: all features are allowed.
+  const std::string empty_list_json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "2.5",
+        "entries": [
+        ]
+      }
+  );
+  scoped_ptr<GpuControlList> control_list(Create());
+
+  EXPECT_TRUE(control_list->LoadList(empty_list_json,
+                                     GpuControlList::kAllOs));
+  EXPECT_EQ("2.5", control_list->version());
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
+  EXPECT_EMPTY_SET(features);
+}
+
+TEST_F(GpuControlListTest, DetailedEntryAndInvalidJson) {
+  // exact setting.
+  const std::string exact_list_json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 5,
+            "os": {
+              "type": "macosx",
+              "version": {
+                "op": "=",
+                "number": "10.6.4"
+              }
+            },
+            "vendor_id": "0x10de",
+            "device_id": ["0x0640"],
+            "driver_version": {
+              "op": "=",
+              "number": "1.6.18"
+            },
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  scoped_ptr<GpuControlList> control_list(Create());
+
+  EXPECT_TRUE(control_list->LoadList(exact_list_json, GpuControlList::kAllOs));
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+
+  // Invalid json input should not change the current control_list settings.
+  const std::string invalid_json = "invalid";
+
+  EXPECT_FALSE(control_list->LoadList(invalid_json, GpuControlList::kAllOs));
+  features = control_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+  std::vector<uint32> entries;
+  control_list->GetDecisionEntries(&entries, false);
+  ASSERT_EQ(1u, entries.size());
+  EXPECT_EQ(5u, entries[0]);
+  EXPECT_EQ(5u, control_list->max_entry_id());
+}
+
+TEST_F(GpuControlListTest, VendorOnAllOsEntry) {
+  // ControlList a vendor on all OS.
+  const std::string vendor_json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "vendor_id": "0x10de",
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  scoped_ptr<GpuControlList> control_list(Create());
+
+  // ControlList entries won't be filtered to the current OS only upon loading.
+  EXPECT_TRUE(control_list->LoadList(vendor_json, GpuControlList::kAllOs));
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+  features = control_list->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+  features = control_list->MakeDecision(
+      GpuControlList::kOsLinux, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX) || \
+    defined(OS_OPENBSD)
+  // ControlList entries will be filtered to the current OS only upon loading.
+  EXPECT_TRUE(control_list->LoadList(
+      vendor_json, GpuControlList::kCurrentOsOnly));
+  features = control_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+  features = control_list->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+  features = control_list->MakeDecision(
+      GpuControlList::kOsLinux, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+#endif
+}
+
+TEST_F(GpuControlListTest, ChromeVersionEntry) {
+  const std::string browser_version_json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "browser_version": {
+              "op": ">=",
+              "number": "10"
+            },
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  scoped_ptr<GpuControlList> control_list9(Create());
+  EXPECT_TRUE(control_list9->LoadList(
+      "9.0", browser_version_json, GpuControlList::kAllOs));
+  std::set<int> features = control_list9->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_EMPTY_SET(features);
+
+  scoped_ptr<GpuControlList> control_list10(Create());
+  EXPECT_TRUE(control_list10->LoadList(
+      "10.0", browser_version_json, GpuControlList::kAllOs));
+  features = control_list10->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+}
+
+TEST_F(GpuControlListTest, UnknownField) {
+  const std::string unknown_field_json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "unknown_field": 0,
+            "features": [
+              "test_feature_1"
+            ]
+          },
+          {
+            "id": 2,
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  scoped_ptr<GpuControlList> control_list(Create());
+
+  EXPECT_TRUE(control_list->LoadList(
+      unknown_field_json, GpuControlList::kAllOs));
+  EXPECT_EQ(1u, control_list->num_entries());
+  EXPECT_TRUE(control_list->contains_unknown_fields());
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+}
+
+TEST_F(GpuControlListTest, UnknownExceptionField) {
+  const std::string unknown_exception_field_json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "unknown_field": 0,
+            "features": [
+              "test_feature_2"
+            ]
+          },
+          {
+            "id": 2,
+            "exceptions": [
+              {
+                "unknown_field": 0
+              }
+            ],
+            "features": [
+              "test_feature_1"
+            ]
+          },
+          {
+            "id": 3,
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  scoped_ptr<GpuControlList> control_list(Create());
+
+  EXPECT_TRUE(control_list->LoadList(
+      unknown_exception_field_json, GpuControlList::kAllOs));
+  EXPECT_EQ(1u, control_list->num_entries());
+  EXPECT_TRUE(control_list->contains_unknown_fields());
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+}
+
+TEST_F(GpuControlListTest, DisabledEntry) {
+  const std::string disabled_json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "disabled": true,
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  scoped_ptr<GpuControlList> control_list(Create());
+  EXPECT_TRUE(control_list->LoadList(disabled_json, GpuControlList::kAllOs));
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_EMPTY_SET(features);
+  std::vector<uint32> flag_entries;
+  control_list->GetDecisionEntries(&flag_entries, false);
+  EXPECT_EQ(0u, flag_entries.size());
+  control_list->GetDecisionEntries(&flag_entries, true);
+  EXPECT_EQ(1u, flag_entries.size());
+}
+
+TEST_F(GpuControlListTest, NeedsMoreInfoForExceptions) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "os": {
+              "type": "linux"
+            },
+            "vendor_id": "0x8086",
+            "exceptions": [
+              {
+                "gl_renderer": {
+                  "op": "contains",
+                  "value": "mesa"
+                }
+              }
+            ],
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = kIntelVendorId;
+
+  scoped_ptr<GpuControlList> control_list(Create());
+  EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs));
+
+  // The case this entry does not apply.
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info);
+  EXPECT_EMPTY_SET(features);
+  EXPECT_FALSE(control_list->needs_more_info());
+
+  // The case this entry might apply, but need more info.
+  features = control_list->MakeDecision(
+      GpuControlList::kOsLinux, kOsVersion, gpu_info);
+  EXPECT_EMPTY_SET(features);
+  EXPECT_TRUE(control_list->needs_more_info());
+
+  // The case we have full info, and the exception applies (so the entry
+  // does not apply).
+  gpu_info.gl_renderer = "mesa";
+  features = control_list->MakeDecision(
+      GpuControlList::kOsLinux, kOsVersion, gpu_info);
+  EXPECT_EMPTY_SET(features);
+  EXPECT_FALSE(control_list->needs_more_info());
+
+  // The case we have full info, and this entry applies.
+  gpu_info.gl_renderer = "my renderer";
+  features = control_list->MakeDecision(GpuControlList::kOsLinux, kOsVersion,
+      gpu_info);
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+  EXPECT_FALSE(control_list->needs_more_info());
+}
+
+TEST_F(GpuControlListTest, IgnorableEntries) {
+  // If an entry will not change the control_list decisions, then it should not
+  // trigger the needs_more_info flag.
+  const std::string json = LONG_STRING_CONST(
+      {
+        "name": "gpu control list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "os": {
+              "type": "linux"
+            },
+            "vendor_id": "0x8086",
+            "features": [
+              "test_feature_0"
+            ]
+          },
+          {
+            "id": 2,
+            "os": {
+              "type": "linux"
+            },
+            "vendor_id": "0x8086",
+            "driver_version": {
+              "op": "<",
+              "number": "10.7"
+            },
+            "features": [
+              "test_feature_0"
+            ]
+          }
+        ]
+      }
+  );
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = kIntelVendorId;
+
+  scoped_ptr<GpuControlList> control_list(Create());
+  EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs));
+  std::set<int> features = control_list->MakeDecision(
+      GpuControlList::kOsLinux, kOsVersion, gpu_info);
+  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
+  EXPECT_FALSE(control_list->needs_more_info());
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_control_list_version_info_unittest.cc b/gpu/config/gpu_control_list_version_info_unittest.cc
new file mode 100644
index 0000000..39814c8
--- /dev/null
+++ b/gpu/config/gpu_control_list_version_info_unittest.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_control_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class VersionInfoTest : public testing::Test {
+ public:
+  VersionInfoTest() { }
+  virtual ~VersionInfoTest() { }
+
+  typedef GpuControlList::VersionInfo VersionInfo;
+};
+
+TEST_F(VersionInfoTest, ValidVersionInfo) {
+  const std::string op[] = {
+    "=",
+    "<",
+    "<=",
+    ">",
+    ">=",
+    "any",
+    "between"
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    std::string string1;
+    std::string string2;
+    if (op[i] != "any")
+      string1 = "8.9";
+    if (op[i] == "between")
+      string2 = "9.0";
+    VersionInfo info(op[i], std::string(), string1, string2);
+    EXPECT_TRUE(info.IsValid());
+  }
+
+  const std::string style[] = {
+    "lexical",
+    "numerical",
+    ""  // Default, same as "numerical"
+  };
+  for (size_t i =0; i < arraysize(style); ++i) {
+    VersionInfo info("=", style[i], "8.9", std::string());
+    EXPECT_TRUE(info.IsValid());
+    if (style[i] == "lexical")
+      EXPECT_TRUE(info.IsLexical());
+    else
+      EXPECT_FALSE(info.IsLexical());
+  }
+
+  const std::string number[] = {
+    "10",
+    "10.9",
+    "10.0",
+    "10.0.9",
+    "0.8",
+    // Leading 0s are valid.
+    "10.09",
+    // Whitespaces are ignored.
+    " 10.9",
+    "10.9 ",
+    "10 .9",
+    "10. 9",
+  };
+  for (size_t i =0; i < arraysize(number); ++i) {
+    VersionInfo info("=", std::string(), number[i], std::string());
+    EXPECT_TRUE(info.IsValid());
+  }
+}
+
+TEST_F(VersionInfoTest, InvalidVersionInfo) {
+  const std::string op[] = {
+    "=",
+    "<",
+    "<=",
+    ">",
+    ">=",
+    "any",
+    "between"
+  };
+  for (size_t i = 0; i < arraysize(op); ++i) {
+    {
+      VersionInfo info(op[i], std::string(), "8.9", std::string());
+      if (op[i] == "between")
+        EXPECT_FALSE(info.IsValid());
+      else
+        EXPECT_TRUE(info.IsValid());
+    }
+    {
+      VersionInfo info(op[i], std::string(), std::string(), std::string());
+      if (op[i] == "any")
+        EXPECT_TRUE(info.IsValid());
+      else
+        EXPECT_FALSE(info.IsValid());
+    }
+    {
+      VersionInfo info(op[i], std::string(), "8.9", "9.0");
+      EXPECT_TRUE(info.IsValid());
+    }
+  }
+
+  const std::string number[] = {
+    "8.E",
+    "8-9",
+  };
+  for (size_t i = 0; i < arraysize(number); ++i) {
+    VersionInfo info("=", std::string(), number[i], std::string());
+    EXPECT_FALSE(info.IsValid());
+  }
+}
+
+TEST_F(VersionInfoTest, VersionComparison) {
+  {
+    VersionInfo info("any", std::string(), std::string(), std::string());
+    EXPECT_TRUE(info.Contains("0"));
+    EXPECT_TRUE(info.Contains("8.9"));
+    EXPECT_TRUE(info.Contains("100"));
+  }
+  {
+    VersionInfo info(">", std::string(), "8.9", std::string());
+    EXPECT_FALSE(info.Contains("7"));
+    EXPECT_FALSE(info.Contains("8.9"));
+    EXPECT_FALSE(info.Contains("8.9.1"));
+    EXPECT_TRUE(info.Contains("9"));
+  }
+  {
+    VersionInfo info(">=", std::string(), "8.9", std::string());
+    EXPECT_FALSE(info.Contains("7"));
+    EXPECT_TRUE(info.Contains("8.9"));
+    EXPECT_TRUE(info.Contains("8.9.1"));
+    EXPECT_TRUE(info.Contains("9"));
+  }
+  {
+    VersionInfo info("=", std::string(), "8.9", std::string());
+    EXPECT_FALSE(info.Contains("7"));
+    EXPECT_TRUE(info.Contains("8"));
+    EXPECT_TRUE(info.Contains("8.9"));
+    EXPECT_TRUE(info.Contains("8.9.1"));
+    EXPECT_FALSE(info.Contains("9"));
+  }
+  {
+    VersionInfo info("<", std::string(), "8.9", std::string());
+    EXPECT_TRUE(info.Contains("7"));
+    EXPECT_TRUE(info.Contains("8.8"));
+    EXPECT_FALSE(info.Contains("8"));
+    EXPECT_FALSE(info.Contains("8.9"));
+    EXPECT_FALSE(info.Contains("8.9.1"));
+    EXPECT_FALSE(info.Contains("9"));
+  }
+  {
+    VersionInfo info("<=", std::string(), "8.9", std::string());
+    EXPECT_TRUE(info.Contains("7"));
+    EXPECT_TRUE(info.Contains("8.8"));
+    EXPECT_TRUE(info.Contains("8"));
+    EXPECT_TRUE(info.Contains("8.9"));
+    EXPECT_TRUE(info.Contains("8.9.1"));
+    EXPECT_FALSE(info.Contains("9"));
+  }
+  {
+    VersionInfo info("between", std::string(), "8.9", "9.1");
+    EXPECT_FALSE(info.Contains("7"));
+    EXPECT_FALSE(info.Contains("8.8"));
+    EXPECT_TRUE(info.Contains("8"));
+    EXPECT_TRUE(info.Contains("8.9"));
+    EXPECT_TRUE(info.Contains("8.9.1"));
+    EXPECT_TRUE(info.Contains("9"));
+    EXPECT_TRUE(info.Contains("9.1"));
+    EXPECT_TRUE(info.Contains("9.1.9"));
+    EXPECT_FALSE(info.Contains("9.2"));
+    EXPECT_FALSE(info.Contains("10"));
+  }
+}
+
+TEST_F(VersionInfoTest, DateComparison) {
+  // When we use '-' as splitter, we assume a format of mm-dd-yyyy
+  // or mm-yyyy, i.e., a date.
+  {
+    VersionInfo info("=", std::string(), "1976.3.21", std::string());
+    EXPECT_TRUE(info.Contains("3-21-1976", '-'));
+    EXPECT_TRUE(info.Contains("3-1976", '-'));
+    EXPECT_TRUE(info.Contains("03-1976", '-'));
+    EXPECT_FALSE(info.Contains("21-3-1976", '-'));
+  }
+  {
+    VersionInfo info(">", std::string(), "1976.3.21", std::string());
+    EXPECT_TRUE(info.Contains("3-22-1976", '-'));
+    EXPECT_TRUE(info.Contains("4-1976", '-'));
+    EXPECT_TRUE(info.Contains("04-1976", '-'));
+    EXPECT_FALSE(info.Contains("3-1976", '-'));
+    EXPECT_FALSE(info.Contains("2-1976", '-'));
+  }
+  {
+    VersionInfo info("between", std::string(), "1976.3.21", "2012.12.25");
+    EXPECT_FALSE(info.Contains("3-20-1976", '-'));
+    EXPECT_TRUE(info.Contains("3-21-1976", '-'));
+    EXPECT_TRUE(info.Contains("3-22-1976", '-'));
+    EXPECT_TRUE(info.Contains("3-1976", '-'));
+    EXPECT_TRUE(info.Contains("4-1976", '-'));
+    EXPECT_TRUE(info.Contains("1-1-2000", '-'));
+    EXPECT_TRUE(info.Contains("1-2000", '-'));
+    EXPECT_TRUE(info.Contains("2000", '-'));
+    EXPECT_TRUE(info.Contains("11-2012", '-'));
+    EXPECT_TRUE(info.Contains("12-2012", '-'));
+    EXPECT_TRUE(info.Contains("12-24-2012", '-'));
+    EXPECT_TRUE(info.Contains("12-25-2012", '-'));
+    EXPECT_FALSE(info.Contains("12-26-2012", '-'));
+    EXPECT_FALSE(info.Contains("1-2013", '-'));
+    EXPECT_FALSE(info.Contains("2013", '-'));
+  }
+}
+
+TEST_F(VersionInfoTest, LexicalComparison) {
+  // When we use lexical style, we assume a format major.minor.*.
+  // We apply numerical comparison to major, lexical comparison to others.
+  {
+    VersionInfo info("<", "lexical", "8.201", std::string());
+    EXPECT_TRUE(info.Contains("8.001.100"));
+    EXPECT_TRUE(info.Contains("8.109"));
+    EXPECT_TRUE(info.Contains("8.10900"));
+    EXPECT_TRUE(info.Contains("8.109.100"));
+    EXPECT_TRUE(info.Contains("8.2"));
+    EXPECT_TRUE(info.Contains("8.20"));
+    EXPECT_TRUE(info.Contains("8.200"));
+    EXPECT_TRUE(info.Contains("8.20.100"));
+    EXPECT_FALSE(info.Contains("8.201"));
+    EXPECT_FALSE(info.Contains("8.2010"));
+    EXPECT_FALSE(info.Contains("8.21"));
+    EXPECT_FALSE(info.Contains("8.21.100"));
+    EXPECT_FALSE(info.Contains("9.002"));
+    EXPECT_FALSE(info.Contains("9.201"));
+    EXPECT_FALSE(info.Contains("12"));
+    EXPECT_FALSE(info.Contains("12.201"));
+  }
+  {
+    VersionInfo info("<", "lexical", "9.002", std::string());
+    EXPECT_TRUE(info.Contains("8.001.100"));
+    EXPECT_TRUE(info.Contains("8.109"));
+    EXPECT_TRUE(info.Contains("8.10900"));
+    EXPECT_TRUE(info.Contains("8.109.100"));
+    EXPECT_TRUE(info.Contains("8.2"));
+    EXPECT_TRUE(info.Contains("8.20"));
+    EXPECT_TRUE(info.Contains("8.200"));
+    EXPECT_TRUE(info.Contains("8.20.100"));
+    EXPECT_TRUE(info.Contains("8.201"));
+    EXPECT_TRUE(info.Contains("8.2010"));
+    EXPECT_TRUE(info.Contains("8.21"));
+    EXPECT_TRUE(info.Contains("8.21.100"));
+    EXPECT_FALSE(info.Contains("9.002"));
+    EXPECT_FALSE(info.Contains("9.201"));
+    EXPECT_FALSE(info.Contains("12"));
+    EXPECT_FALSE(info.Contains("12.201"));
+  }
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_driver_bug_list.cc b/gpu/config/gpu_driver_bug_list.cc
new file mode 100644
index 0000000..68f1b0b
--- /dev/null
+++ b/gpu/config/gpu_driver_bug_list.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_driver_bug_list.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
+
+namespace gpu {
+
+namespace {
+
+struct DriverBugInfo {
+  int feature_type;
+  std::string feature_name;
+};
+
+}  // namespace anonymous
+
+GpuDriverBugList::GpuDriverBugList()
+    : GpuControlList() {
+}
+
+GpuDriverBugList::~GpuDriverBugList() {
+}
+
+// static
+GpuDriverBugList* GpuDriverBugList::Create() {
+  GpuDriverBugList* list = new GpuDriverBugList();
+
+  const DriverBugInfo kFeatureList[] = {
+#define GPU_OP(type, name) { type, #name },
+    GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP)
+#undef GPU_OP
+  };
+  DCHECK_EQ(static_cast<int>(arraysize(kFeatureList)),
+            NUMBER_OF_GPU_DRIVER_BUG_WORKAROUND_TYPES);
+  for (int i = 0; i < NUMBER_OF_GPU_DRIVER_BUG_WORKAROUND_TYPES; ++i) {
+    list->AddSupportedFeature(kFeatureList[i].feature_name,
+                              kFeatureList[i].feature_type);
+  }
+  return list;
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_driver_bug_list.h b/gpu/config/gpu_driver_bug_list.h
new file mode 100644
index 0000000..9943251
--- /dev/null
+++ b/gpu/config/gpu_driver_bug_list.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 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 GPU_CONFIG_GPU_DRIVER_BUG_LIST_H_
+#define GPU_CONFIG_GPU_DRIVER_BUG_LIST_H_
+
+#include <string>
+
+#include "gpu/config/gpu_control_list.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+class GPU_EXPORT GpuDriverBugList : public GpuControlList {
+ public:
+  virtual ~GpuDriverBugList();
+
+  static GpuDriverBugList* Create();
+
+ private:
+  GpuDriverBugList();
+
+  DISALLOW_COPY_AND_ASSIGN(GpuDriverBugList);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_DRIVER_BUG_LIST_H_
+
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
new file mode 100644
index 0000000..dc9e5bf
--- /dev/null
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -0,0 +1,366 @@
+// Copyright (c) 2013 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.
+
+// Determines whether a certain driver bug exists in the current system.
+// A valid gpu_driver_bug_list.json file are in the format of
+// {
+//   "version": "x.y",
+//   "entries": [
+//     { // entry 1
+//     },
+//     ...
+//     { // entry n
+//     }
+//   ]
+// }
+//
+// Each entry contains the following fields (fields are optional unless
+// specifically described as mandatory below):
+// 1. "id" is an integer.  0 is reserved.  This field is mandatory.
+// 2. "os" contains "type" and an optional "version". "type" could be "macosx",
+//    "linux", "win", "chromeos", or "any".  "any" is the same as not specifying
+//    "os".
+//    "version" is a VERSION structure (defined below).
+// 3. "vendor_id" is a string.  0 is reserved.
+// 4. "device_id" is an array of strings.  0 is reserved.
+// 5. "multi_gpu_style" is a string, valid values include "optimus", and
+//    "amd_switchable".
+// 6. "multi_gpu_category" is a string, valid values include "any", "primary",
+//    and "secondary".  If unspecified, the default value is "primary".
+// 7. "driver_vendor" is a STRING structure (defined below).
+// 8. "driver_version" is a VERSION structure (defined below).
+// 9. "driver_date" is a VERSION structure (defined below).
+//    The version is interpreted as "year.month.day".
+// 10. "gl_vendor" is a STRING structure (defined below).
+// 11. "gl_renderer" is a STRING structure (defined below).
+// 12. "gl_extensions" is a STRING structure (defined below).
+// 13. "perf_graphics" is a FLOAT structure (defined below).
+// 14. "perf_gaming" is a FLOAT structure (defined below).
+// 15. "perf_overall" is a FLOAT structure (defined below).
+// 16. "machine_model" contais "name" and an optional "version".  "name" is a
+//     STRING structure and "version" is a VERSION structure (defined below).
+// 17. "gpu_count" is a INT structure (defined below).
+// 18  "cpu_info" is a STRING structure (defined below).
+// 19. "exceptions" is a list of entries.
+// 20. "features" is a list of driver bug types. For a list of supported types,
+//     see src/gpu/command_buffer/service/gpu_driver_bug_workaround_type.h
+//     This field is mandatory.
+// 21. "description" has the description of the entry.
+// 22. "webkit_bugs" is an array of associated webkit bug numbers.
+// 23. "cr_bugs" is an array of associated chromium bug numbers.
+// 24. "browser_version" is a VERSION structure (defined below).  If this
+//     condition is not satisfied, the entry will be ignored.  If it is not
+//     present, then the entry applies to all versions of the browser.
+// 25. "disabled" is a boolean. If it is present, the entry will be skipped.
+//     This can not be used in exceptions.
+//
+// VERSION includes "op", "style", "number", and "number2".  "op" can be any of
+// the following values: "=", "<", "<=", ">", ">=", "any", "between".  "style"
+// is optional and can be "lexical" or "numerical"; if it's not specified, it
+// defaults to "numerical".  "number2" is only used if "op" is "between".
+// "between" is "number <= * <= number2".
+// "number" is used for all "op" values except "any". "number" and "number2"
+// are in the format of x, x.x, x.x.x, etc.
+// Only "driver_version" supports lexical style if the format is major.minor;
+// in that case, major is still numerical, but minor is lexical. 
+//
+// STRING includes "op" and "value".  "op" can be any of the following values:
+// "contains", "beginwith", "endwith", "=".  "value" is a string.
+//
+// FLOAT includes "op" "value", and "value2".  "op" can be any of the
+// following values: "=", "<", "<=", ">", ">=", "any", "between".  "value2" is
+// only used if "op" is "between".  "value" is used for all "op" values except
+// "any". "value" and "value2" are valid float numbers.
+// INT is very much like FLOAT, except that the values need to be integers.
+
+#include "gpu/config/gpu_control_list_jsons.h"
+
+#define LONG_STRING_CONST(...) #__VA_ARGS__
+
+namespace gpu {
+
+const char kGpuDriverBugListJson[] = LONG_STRING_CONST(
+
+{
+  "name": "gpu driver bug list",
+  // Please update the version number whenever you change this file.
+  "version": "2.1",
+  "entries": [
+    {
+      "id": 1,
+      "description": "Imagination driver doesn't like uploading lots of buffer data constantly",
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "Imagination"
+      },
+      "features": [
+        "use_client_side_arrays_for_stream_buffers"
+      ]
+    },
+    {
+      "id": 2,
+      "description": "ARM driver doesn't like uploading lots of buffer data constantly",
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "ARM"
+      },
+      "features": [
+        "use_client_side_arrays_for_stream_buffers"
+      ]
+    },
+    {
+      "id": 3,
+      "features": [
+        "set_texture_filter_before_generating_mipmap"
+      ]
+    },
+    {
+      "id": 4,
+      "description": "Need to set the alpha to 255",
+      "features": [
+        "clear_alpha_in_readpixels"
+      ]
+    },
+    {
+      "id": 5,
+      "vendor_id": "0x10de",
+      "features": [
+        "use_current_program_after_successful_link"
+      ]
+    },
+    {
+      "id": 6,
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "Qualcomm"
+      },
+      "features": [
+        "restore_scissor_on_fbo_change",
+        "flush_on_context_switch",
+        "delete_instead_of_resize_fbo"  // Only need this on the ICS driver.
+      ]
+    },
+    {
+      "id": 7,
+      "cr_bugs": [89557],
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x10de",
+      "features": [
+        "needs_offscreen_buffer_workaround"
+      ]
+    },
+    {
+      "id": 8,
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x1002",
+      "features": [
+        "needs_glsl_built_in_function_emulation"
+      ]
+    },
+    {
+      "id": 9,
+      "description": "Mac AMD drivers get gl_PointCoord backward, rdar://problem/11883495",
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x1002",
+      "features": [
+        "reverse_point_sprite_coord_origin"
+      ]
+    },
+    {
+      "id": 10,
+      "description": "Mac Intel drivers get gl_PointCoord backward, rdar://problem/11883495",
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "reverse_point_sprite_coord_origin"
+      ]
+    },
+    {
+      "id": 11,
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "max_texture_size_limit_4096"
+      ]
+    },
+    {
+      "id": 12,
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "max_cube_map_texture_size_limit_1024"
+      ]
+    },
+    {
+      "id": 13,
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "<",
+          "number": "10.7.3"
+        }
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "max_cube_map_texture_size_limit_512"
+      ]
+    },
+    {
+      "id": 14,
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x1002",
+      "features": [
+        "max_texture_size_limit_4096",
+        "max_cube_map_texture_size_limit_4096"
+      ]
+    },
+    {
+      "id": 15,
+      "description": "Some Android Qualcomm drivers falsely report GL_ANGLE_framebuffer_multisample",
+      "cr_bugs": [165736],
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "Qualcomm"
+      },
+      "features": [
+        "disable_angle_framebuffer_multisample"
+      ]
+    },
+    {
+      "id": 16,
+      "description": "Intel drivers on Linux appear to be buggy",
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "disable_ext_occlusion_query"
+      ]
+    },
+    {
+      "id": 17,
+      "description": "Some drivers are unable to reset the D3D device in the GPU process sandbox",
+      "os": {
+        "type": "win"
+      },
+      "features": [
+        "exit_on_context_lost"
+      ]
+    },
+    {
+      "id": 18,
+      "description": "Everything except async + NPOT + multiple-of-8 textures are brutally slow for Imagination drivers",
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "Imagination"
+      },
+      "features": [
+        "enable_chromium_fast_npot_mo8_textures"
+      ]
+    },
+    {
+      "id": 19,
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "Qualcomm"
+      },
+      "features": [
+        "disable_depth_texture"
+      ]
+    },
+    {
+      "id": 20,
+      "description": "Disable EXT_draw_buffers on GeForce GT 650M on Mac OS X due to driver bugs.",
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x0fd5"],
+      "multi_gpu_category": "any",
+      "features": [
+        "disable_ext_draw_buffers"
+      ]
+    },
+    {
+      "id": 21,
+      "description": "Vivante GPUs are buggy with context switching.",
+      "cr_bugs": [179250, 235935],
+      "os": {
+        "type": "android"
+      },
+      "gl_extensions": {
+        "op": "contains",
+        "value": "GL_VIV_shader_binary"
+      },
+      "features": [
+        "unbind_fbo_on_context_switch"
+      ]
+    },
+    {
+      "id": 22,
+      "description": "Imagination drivers are buggy with context switching.",
+      "cr_bugs": [230896],
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "Imagination"
+      },
+      "features": [
+        "unbind_fbo_on_context_switch"
+      ]
+    },
+    {
+      "id": 23,
+      "cr_bugs": [243038],
+      "description": "Disable OES_standard_derivative on Intel Pineview M Gallium drivers.",
+      "os": {
+        "type": "chromeos"
+      },
+      "vendor_id": "0x8086",
+      "device_id": ["0xa011", "0xa012"],
+      "features": [
+        "disable_oes_standard_derivatives"
+      ]
+    }
+  ]
+}
+
+);  // LONG_STRING_CONST macro
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_driver_bug_list_unittest.cc b/gpu/config/gpu_driver_bug_list_unittest.cc
new file mode 100644
index 0000000..60bc339
--- /dev/null
+++ b/gpu/config/gpu_driver_bug_list_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 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/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "gpu/config/gpu_control_list_jsons.h"
+#include "gpu/config/gpu_driver_bug_list.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
+#include "gpu/config/gpu_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const char kOsVersion[] = "10.6.4";
+
+namespace gpu {
+
+class GpuDriverBugListTest : public testing::Test {
+ public:
+  GpuDriverBugListTest() { }
+
+  virtual ~GpuDriverBugListTest() { }
+
+  const GPUInfo& gpu_info() const {
+    return gpu_info_;
+  }
+
+ protected:
+  virtual void SetUp() {
+    gpu_info_.gpu.vendor_id = 0x10de;
+    gpu_info_.gpu.device_id = 0x0640;
+    gpu_info_.driver_vendor = "NVIDIA";
+    gpu_info_.driver_version = "1.6.18";
+    gpu_info_.driver_date = "7-14-2009";
+    gpu_info_.machine_model = "MacBookPro 7.1";
+    gpu_info_.gl_vendor = "NVIDIA Corporation";
+    gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
+    gpu_info_.performance_stats.graphics = 5.0;
+    gpu_info_.performance_stats.gaming = 5.0;
+    gpu_info_.performance_stats.overall = 5.0;
+  }
+
+  virtual void TearDown() {
+  }
+
+ private:
+  GPUInfo gpu_info_;
+};
+
+TEST_F(GpuDriverBugListTest, CurrentDriverBugListValidation) {
+  scoped_ptr<GpuDriverBugList> list(GpuDriverBugList::Create());
+  std::string json;
+  EXPECT_TRUE(list->LoadList(kGpuDriverBugListJson, GpuControlList::kAllOs));
+  EXPECT_FALSE(list->contains_unknown_fields());
+}
+
+TEST_F(GpuDriverBugListTest, CurrentListForARM) {
+  scoped_ptr<GpuDriverBugList> list(GpuDriverBugList::Create());
+  EXPECT_TRUE(list->LoadList(kGpuDriverBugListJson, GpuControlList::kAllOs));
+
+  GPUInfo gpu_info;
+  gpu_info.gl_vendor = "ARM";
+  gpu_info.gl_renderer = "MALi_T604";
+  std::set<int> bugs = list->MakeDecision(
+      GpuControlList::kOsAndroid, "4.1", gpu_info);
+  EXPECT_EQ(1u, bugs.count(USE_CLIENT_SIDE_ARRAYS_FOR_STREAM_BUFFERS));
+}
+
+TEST_F(GpuDriverBugListTest, CurrentListForImagination) {
+  scoped_ptr<GpuDriverBugList> list(GpuDriverBugList::Create());
+  EXPECT_TRUE(list->LoadList(kGpuDriverBugListJson, GpuControlList::kAllOs));
+
+  GPUInfo gpu_info;
+  gpu_info.gl_vendor = "Imagination Technologies";
+  gpu_info.gl_renderer = "PowerVR SGX 540";
+  std::set<int> bugs = list->MakeDecision(
+      GpuControlList::kOsAndroid, "4.1", gpu_info);
+  EXPECT_EQ(1u, bugs.count(USE_CLIENT_SIDE_ARRAYS_FOR_STREAM_BUFFERS));
+}
+
+}  // namespace gpu
+
diff --git a/gpu/command_buffer/service/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h
similarity index 91%
rename from gpu/command_buffer/service/gpu_driver_bug_workaround_type.h
rename to gpu/config/gpu_driver_bug_workaround_type.h
index 5580453..1d78c97 100644
--- a/gpu/command_buffer/service/gpu_driver_bug_workaround_type.h
+++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef GPU_COMMAND_BUFFER_SERVICE_GPU_DRIVER_BUG_WORDAROUND_TYPE_H_
-#define GPU_COMMAND_BUFFER_SERVICE_GPU_DRIVER_BUG_WORDAROUND_TYPE_H_
+#ifndef GPU_CONFIG_GPU_DRIVER_BUG_WORKAROUND_TYPE_H_
+#define GPU_CONFIG_GPU_DRIVER_BUG_WORKAROUND_TYPE_H_
 
 #include "gpu/gpu_export.h"
 
@@ -22,6 +22,8 @@
          disable_ext_draw_buffers)                    \
   GPU_OP(DISABLE_EXT_OCCLUSION_QUERY,                 \
          disable_ext_occlusion_query)                 \
+  GPU_OP(DISABLE_OES_STANDARD_DERIVATIVES,            \
+         disable_oes_standard_derivatives)            \
   GPU_OP(ENABLE_CHROMIUM_FAST_NPOT_MO8_TEXTURES,      \
          enable_chromium_fast_npot_mo8_textures)      \
   GPU_OP(EXIT_ON_CONTEXT_LOST,                        \
@@ -65,5 +67,5 @@
 
 }  // namespace gpu
 
-#endif  // GPU_COMMAND_BUFFER_SERVICE_GPU_DRIVER_BUG_WORDAROUND_TYPE_H_
+#endif  // GPU_CONFIG_GPU_DRIVER_BUG_WORKAROUND_TYPE_H_
 
diff --git a/gpu/config/gpu_dx_diagnostics_win.cc b/gpu/config/gpu_dx_diagnostics_win.cc
new file mode 100644
index 0000000..ed2fc91
--- /dev/null
+++ b/gpu/config/gpu_dx_diagnostics_win.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2011 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.
+//
+// Functions to enumerate the Dx Diagnostic Tool hierarchy and build up
+// a tree of nodes with name / value properties.
+
+#define INITGUID
+#include <dxdiag.h>
+#include <windows.h>
+
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_com_initializer.h"
+#include "gpu/config/gpu_info_collector.h"
+
+namespace gpu {
+
+namespace {
+
+// Traverses the IDxDiagContainer tree and populates a tree of DxDiagNode
+// structures that contains property name / value pairs and subtrees of DirectX
+// diagnostic information.
+void RecurseDiagnosticTree(DxDiagNode* output,
+                           IDxDiagContainer* container,
+                           int depth) {
+  HRESULT hr;
+
+  VARIANT variant;
+  VariantInit(&variant);
+
+  DWORD prop_count;
+  hr = container->GetNumberOfProps(&prop_count);
+  if (SUCCEEDED(hr)) {
+    for (DWORD i = 0; i < prop_count; i++) {
+      WCHAR prop_name16[256];
+      hr = container->EnumPropNames(i, prop_name16, arraysize(prop_name16));
+      if (SUCCEEDED(hr)) {
+        std::string prop_name8 = WideToUTF8(prop_name16);
+
+        hr = container->GetProp(prop_name16, &variant);
+        if (SUCCEEDED(hr)) {
+          switch (variant.vt) {
+            case VT_UI4:
+              output->values[prop_name8] = base::UintToString(variant.ulVal);
+              break;
+            case VT_I4:
+              output->values[prop_name8] = base::IntToString(variant.lVal);
+              break;
+            case VT_BOOL:
+              output->values[prop_name8] = variant.boolVal ? "true" : "false";
+              break;
+            case VT_BSTR:
+              output->values[prop_name8] = WideToUTF8(variant.bstrVal);
+              break;
+            default:
+              break;
+          }
+
+          // Clear the variant (this is needed to free BSTR memory).
+          VariantClear(&variant);
+        }
+      }
+    }
+  }
+
+  if (depth > 0) {
+    DWORD child_count;
+    hr = container->GetNumberOfChildContainers(&child_count);
+    if (SUCCEEDED(hr)) {
+      for (DWORD i = 0; i < child_count; i++) {
+        WCHAR child_name16[256];
+        hr = container->EnumChildContainerNames(i,
+                                                child_name16,
+                                                arraysize(child_name16));
+        if (SUCCEEDED(hr)) {
+          std::string child_name8 = WideToUTF8(child_name16);
+          DxDiagNode* output_child = &output->children[child_name8];
+
+          IDxDiagContainer* child_container = NULL;
+          hr = container->GetChildContainer(child_name16, &child_container);
+          if (SUCCEEDED(hr)) {
+            RecurseDiagnosticTree(output_child, child_container, depth - 1);
+
+            child_container->Release();
+          }
+        }
+      }
+    }
+  }
+}
+}  // namespace anonymous
+
+bool GetDxDiagnostics(DxDiagNode* output) {
+  HRESULT hr;
+  bool success = false;
+  base::win::ScopedCOMInitializer com_initializer;
+
+  IDxDiagProvider* provider = NULL;
+  hr = CoCreateInstance(CLSID_DxDiagProvider,
+                         NULL,
+                         CLSCTX_INPROC_SERVER,
+                         IID_IDxDiagProvider,
+                         reinterpret_cast<void**>(&provider));
+  if (SUCCEEDED(hr)) {
+    DXDIAG_INIT_PARAMS params = { sizeof(params) };
+    params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION;
+    params.bAllowWHQLChecks = FALSE;
+    params.pReserved = NULL;
+
+    hr = provider->Initialize(&params);
+    if (SUCCEEDED(hr)) {
+      IDxDiagContainer* root = NULL;
+      hr = provider->GetRootContainer(&root);
+      if (SUCCEEDED(hr)) {
+        // Limit to the DisplayDevices subtree. The tree in its entirity is
+        // enormous and only this branch contains useful information.
+        IDxDiagContainer* display_devices = NULL;
+        hr = root->GetChildContainer(L"DxDiag_DisplayDevices",
+                                     &display_devices);
+        if (SUCCEEDED(hr)) {
+          RecurseDiagnosticTree(output, display_devices, 1);
+          success = true;
+          display_devices->Release();
+        }
+
+        root->Release();
+      }
+    }
+    provider->Release();
+  }
+
+  return success;
+}
+}  // namespace gpu
diff --git a/gpu/config/gpu_feature_type.h b/gpu/config/gpu_feature_type.h
new file mode 100644
index 0000000..b082d78
--- /dev/null
+++ b/gpu/config/gpu_feature_type.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_FEATURE_TYPE_H_
+#define GPU_CONFIG_GPU_FEATURE_TYPE_H_
+
+namespace gpu {
+
+// Provides flags indicating which gpu features are blacklisted for the system
+// on which chrome is currently running.
+// If a bit is set to 1, corresponding feature is blacklisted.
+enum GpuFeatureType {
+  GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS = 0,
+  GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING,
+  GPU_FEATURE_TYPE_WEBGL,
+  GPU_FEATURE_TYPE_MULTISAMPLING,
+  GPU_FEATURE_TYPE_FLASH3D,
+  GPU_FEATURE_TYPE_FLASH_STAGE3D,
+  GPU_FEATURE_TYPE_TEXTURE_SHARING,
+  GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE,
+  GPU_FEATURE_TYPE_3D_CSS,
+  GPU_FEATURE_TYPE_ACCELERATED_VIDEO,
+  GPU_FEATURE_TYPE_PANEL_FITTING,
+  GPU_FEATURE_TYPE_FORCE_COMPOSITING_MODE,
+  GPU_FEATURE_TYPE_FLASH_STAGE3D_BASELINE,
+  NUMBER_OF_GPU_FEATURE_TYPES
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_FEATURE_TYPE_H_
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
new file mode 100644
index 0000000..8c2f9cc
--- /dev/null
+++ b/gpu/config/gpu_info.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info.h"
+
+namespace gpu {
+
+GPUInfo::GPUDevice::GPUDevice()
+    : vendor_id(0),
+      device_id(0) {
+}
+
+GPUInfo::GPUDevice::~GPUDevice() { }
+
+GPUInfo::GPUInfo()
+    : finalized(false),
+      optimus(false),
+      amd_switchable(false),
+      lenovo_dcute(false),
+      adapter_luid(0),
+      can_lose_context(false),
+      gpu_accessible(true),
+      software_rendering(false),
+      sandboxed(false) {
+}
+
+GPUInfo::~GPUInfo() { }
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h
new file mode 100644
index 0000000..f8d9b60
--- /dev/null
+++ b/gpu/config/gpu_info.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_INFO_H_
+#define GPU_CONFIG_GPU_INFO_H_
+
+// Provides access to the GPU information for the system
+// on which chrome is currently running.
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "base/version.h"
+#include "build/build_config.h"
+#include "gpu/config/dx_diag_node.h"
+#include "gpu/config/gpu_performance_stats.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+struct GPU_EXPORT GPUInfo {
+  struct GPU_EXPORT GPUDevice {
+    GPUDevice();
+    ~GPUDevice();
+
+    // The DWORD (uint32) representing the graphics card vendor id.
+    uint32 vendor_id;
+
+    // The DWORD (uint32) representing the graphics card device id.
+    // Device ids are unique to vendor, not to one another.
+    uint32 device_id;
+
+    // The strings that describe the GPU.
+    // In Linux these strings are obtained through libpci.
+    // In Win/MacOSX, these two strings are not filled at the moment.
+    std::string vendor_string;
+    std::string device_string;
+  };
+
+  GPUInfo();
+  ~GPUInfo();
+
+  // Whether more GPUInfo fields might be collected in the future.
+  bool finalized;
+
+  // The amount of time taken to get from the process starting to the message
+  // loop being pumped.
+  base::TimeDelta initialization_time;
+
+  // Computer has NVIDIA Optimus
+  bool optimus;
+
+  // Computer has AMD Dynamic Switchable Graphics
+  bool amd_switchable;
+
+  // Lenovo dCute is installed. http://crbug.com/181665.
+  bool lenovo_dcute;
+
+  // Version of DisplayLink driver installed. Zero if not installed.
+  // http://crbug.com/177611.
+  Version display_link_version;
+
+  // Primary GPU, for exmaple, the discrete GPU in a dual GPU machine.
+  GPUDevice gpu;
+
+  // Secondary GPUs, for example, the integrated GPU in a dual GPU machine.
+  std::vector<GPUDevice> secondary_gpus;
+
+  // On Windows, the unique identifier of the adapter the GPU process uses.
+  // The default is zero, which makes the browser process create its D3D device
+  // on the primary adapter. Note that the primary adapter can change at any
+  // time so it is better to specify a particular LUID. Note that valid LUIDs
+  // are always non-zero.
+  uint64 adapter_luid;
+
+  // The vendor of the graphics driver currently installed.
+  std::string driver_vendor;
+
+  // The version of the graphics driver currently installed.
+  std::string driver_version;
+
+  // The date of the graphics driver currently installed.
+  std::string driver_date;
+
+  // The version of the pixel/fragment shader used by the gpu.
+  std::string pixel_shader_version;
+
+  // The version of the vertex shader used by the gpu.
+  std::string vertex_shader_version;
+
+  // The machine model identifier with format "name major.minor".
+  // Name should not contain any whitespaces.
+  std::string machine_model;
+
+  // The version of OpenGL we are using.
+  // TODO(zmo): should be able to tell if it's GL or GLES.
+  std::string gl_version;
+
+  // The GL_VERSION string.  "" if we are not using OpenGL.
+  std::string gl_version_string;
+
+  // The GL_VENDOR string.  "" if we are not using OpenGL.
+  std::string gl_vendor;
+
+  // The GL_RENDERER string.  "" if we are not using OpenGL.
+  std::string gl_renderer;
+
+  // The GL_EXTENSIONS string.  "" if we are not using OpenGL.
+  std::string gl_extensions;
+
+  // The device semantics, i.e. whether the Vista and Windows 7 specific
+  // semantics are available.
+  bool can_lose_context;
+
+  // Whether gpu or driver is accessible.
+  bool gpu_accessible;
+
+  // By default all values are 0.
+  GpuPerformanceStats performance_stats;
+
+  bool software_rendering;
+
+  // Whether the gpu process is running in a sandbox.
+  bool sandboxed;
+
+#if defined(OS_WIN)
+  // The information returned by the DirectX Diagnostics Tool.
+  DxDiagNode dx_diagnostics;
+#endif
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_INFO_H_
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc
new file mode 100644
index 0000000..eb40dfc
--- /dev/null
+++ b/gpu/config/gpu_info_collector.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info_collector.h"
+
+#include <string>
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+
+namespace {
+
+scoped_refptr<gfx::GLSurface> InitializeGLSurface() {
+  scoped_refptr<gfx::GLSurface> surface(
+      gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1)));
+  if (!surface) {
+    LOG(ERROR) << "gfx::GLContext::CreateOffscreenGLSurface failed";
+    return NULL;
+  }
+
+  return surface;
+}
+
+scoped_refptr<gfx::GLContext> InitializeGLContext(gfx::GLSurface* surface) {
+
+  scoped_refptr<gfx::GLContext> context(
+      gfx::GLContext::CreateGLContext(NULL,
+                                      surface,
+                                      gfx::PreferIntegratedGpu));
+  if (!context) {
+    LOG(ERROR) << "gfx::GLContext::CreateGLContext failed";
+    return NULL;
+  }
+
+  if (!context->MakeCurrent(surface)) {
+    LOG(ERROR) << "gfx::GLContext::MakeCurrent() failed";
+    return NULL;
+  }
+
+  return context;
+}
+
+std::string GetGLString(unsigned int pname) {
+  const char* gl_string =
+      reinterpret_cast<const char*>(glGetString(pname));
+  if (gl_string)
+    return std::string(gl_string);
+  return std::string();
+}
+
+// Return a version string in the format of "major.minor".
+std::string GetVersionFromString(const std::string& version_string) {
+  size_t begin = version_string.find_first_of("0123456789");
+  if (begin != std::string::npos) {
+    size_t end = version_string.find_first_not_of("01234567890.", begin);
+    std::string sub_string;
+    if (end != std::string::npos)
+      sub_string = version_string.substr(begin, end - begin);
+    else
+      sub_string = version_string.substr(begin);
+    std::vector<std::string> pieces;
+    base::SplitString(sub_string, '.', &pieces);
+    if (pieces.size() >= 2)
+      return pieces[0] + "." + pieces[1];
+  }
+  return std::string();
+}
+
+}  // namespace anonymous
+
+namespace gpu {
+
+bool CollectGraphicsInfoGL(GPUInfo* gpu_info) {
+  TRACE_EVENT0("startup", "gpu_info_collector::CollectGraphicsInfoGL");
+  if (!gfx::GLSurface::InitializeOneOff()) {
+    LOG(ERROR) << "gfx::GLSurface::InitializeOneOff() failed";
+    return false;
+  }
+
+  scoped_refptr<gfx::GLSurface> surface(InitializeGLSurface());
+  if (!surface)
+    return false;
+
+  scoped_refptr<gfx::GLContext> context(InitializeGLContext(surface.get()));
+  if (!context)
+    return false;
+
+  gpu_info->gl_renderer = GetGLString(GL_RENDERER);
+  gpu_info->gl_vendor = GetGLString(GL_VENDOR);
+  gpu_info->gl_extensions = GetGLString(GL_EXTENSIONS);
+  gpu_info->gl_version_string = GetGLString(GL_VERSION);
+  std::string glsl_version_string = GetGLString(GL_SHADING_LANGUAGE_VERSION);
+  // TODO(kbr): remove once the destruction of a current context automatically
+  // clears the current context.
+  context->ReleaseCurrent(surface.get());
+
+  gpu_info->gl_version = GetVersionFromString(gpu_info->gl_version_string);
+  std::string glsl_version = GetVersionFromString(glsl_version_string);
+  gpu_info->pixel_shader_version = glsl_version;
+  gpu_info->vertex_shader_version = glsl_version;
+
+  return CollectDriverInfoGL(gpu_info);
+}
+
+void MergeGPUInfoGL(GPUInfo* basic_gpu_info,
+                    const GPUInfo& context_gpu_info) {
+  DCHECK(basic_gpu_info);
+  basic_gpu_info->gl_renderer = context_gpu_info.gl_renderer;
+  basic_gpu_info->gl_vendor = context_gpu_info.gl_vendor;
+  basic_gpu_info->gl_version_string = context_gpu_info.gl_version_string;
+  basic_gpu_info->gl_extensions = context_gpu_info.gl_extensions;
+  basic_gpu_info->gl_version = context_gpu_info.gl_version;
+  basic_gpu_info->pixel_shader_version =
+      context_gpu_info.pixel_shader_version;
+  basic_gpu_info->vertex_shader_version =
+      context_gpu_info.vertex_shader_version;
+
+  if (!context_gpu_info.driver_vendor.empty())
+    basic_gpu_info->driver_vendor = context_gpu_info.driver_vendor;
+  if (!context_gpu_info.driver_version.empty())
+    basic_gpu_info->driver_version = context_gpu_info.driver_version;
+
+  basic_gpu_info->can_lose_context = context_gpu_info.can_lose_context;
+  basic_gpu_info->sandboxed = context_gpu_info.sandboxed;
+  basic_gpu_info->gpu_accessible = context_gpu_info.gpu_accessible;
+  basic_gpu_info->finalized = context_gpu_info.finalized;
+  basic_gpu_info->initialization_time = context_gpu_info.initialization_time;
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_info_collector.h b/gpu/config/gpu_info_collector.h
new file mode 100644
index 0000000..e89535a
--- /dev/null
+++ b/gpu/config/gpu_info_collector.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_INFO_COLLECTOR_H_
+#define GPU_CONFIG_GPU_INFO_COLLECTOR_H_
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "gpu/config/gpu_info.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+enum GpuIDResult {
+  kGpuIDFailure,
+  kGpuIDSuccess,
+  kGpuIDNotSupported
+};
+
+// Collect GPU vendor_id and device ID.
+GPU_EXPORT GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id);
+
+// Collects basic GPU info without creating a GL/DirectX context (and without
+// the danger of crashing), including vendor_id and device_id.
+// This is called at browser process startup time.
+// The subset each platform collects may be different.
+GPU_EXPORT bool CollectBasicGraphicsInfo(GPUInfo* gpu_info);
+
+// Create a GL/DirectX context and collect related info.
+// This is called at GPU process startup time.
+// Returns true on success.
+GPU_EXPORT bool CollectContextGraphicsInfo(GPUInfo* gpu_info);
+
+#if defined(OS_WIN)
+// Collect the DirectX Disagnostics information about the attached displays.
+GPU_EXPORT bool GetDxDiagnostics(DxDiagNode* output);
+#endif  // OS_WIN
+
+// Create a GL context and collect GL strings and versions.
+GPU_EXPORT bool CollectGraphicsInfoGL(GPUInfo* gpu_info);
+
+// Each platform stores the driver version on the GL_VERSION string differently
+GPU_EXPORT bool CollectDriverInfoGL(GPUInfo* gpu_info);
+
+// Merge GPUInfo from CollectContextGraphicsInfo into basic GPUInfo.
+// This is platform specific, depending on which info are collected at which
+// stage.
+GPU_EXPORT void MergeGPUInfo(GPUInfo* basic_gpu_info,
+                             const GPUInfo& context_gpu_info);
+
+// MergeGPUInfo() when GL driver is used.
+GPU_EXPORT void MergeGPUInfoGL(GPUInfo* basic_gpu_info,
+                               const GPUInfo& context_gpu_info);
+
+// Advanced Micro Devices has interesting configurations on laptops were
+// there are two videocards that can alternatively a given process output.
+enum AMDVideoCardType {
+  UNKNOWN,
+  STANDALONE,
+  INTEGRATED,
+  SWITCHABLE
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_INFO_COLLECTOR_H_
diff --git a/gpu/config/gpu_info_collector_android.cc b/gpu/config/gpu_info_collector_android.cc
new file mode 100644
index 0000000..905fedf
--- /dev/null
+++ b/gpu/config/gpu_info_collector_android.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info_collector.h"
+
+#include "base/android/build_info.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+
+namespace {
+
+std::string GetDriverVersionFromString(const std::string& version_string) {
+  // Extract driver version from the second number in a string like:
+  // "OpenGL ES 2.0 V@6.0 AU@ (CL@2946718)"
+
+  // Exclude first "2.0".
+  size_t begin = version_string.find_first_of("0123456789");
+  if (begin == std::string::npos)
+    return "0";
+  size_t end = version_string.find_first_not_of("01234567890.", begin);
+
+  // Extract number of the form "%d.%d"
+  begin = version_string.find_first_of("0123456789", end);
+  if (begin == std::string::npos)
+    return "0";
+  end = version_string.find_first_not_of("01234567890.", begin);
+  std::string sub_string;
+  if (end != std::string::npos)
+    sub_string = version_string.substr(begin, end - begin);
+  else
+    sub_string = version_string.substr(begin);
+  std::vector<std::string> pieces;
+  base::SplitString(sub_string, '.', &pieces);
+  if (pieces.size() < 2)
+    return "0";
+  return pieces[0] + "." + pieces[1];
+}
+
+}
+
+namespace gpu {
+
+bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
+  return CollectBasicGraphicsInfo(gpu_info);
+}
+
+GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
+  DCHECK(vendor_id && device_id);
+  *vendor_id = 0;
+  *device_id = 0;
+  return kGpuIDNotSupported;
+}
+
+bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
+  gpu_info->can_lose_context = false;
+  gpu_info->finalized = true;
+
+  gpu_info->machine_model = base::android::BuildInfo::GetInstance()->model();
+
+  // Create a short-lived context on the UI thread to collect the GL strings.
+  return CollectGraphicsInfoGL(gpu_info);
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+  gpu_info->driver_version = GetDriverVersionFromString(
+      gpu_info->gl_version_string);
+  return true;
+}
+
+void MergeGPUInfo(GPUInfo* basic_gpu_info,
+                  const GPUInfo& context_gpu_info) {
+  MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
+}
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_info_collector_mac.mm b/gpu/config/gpu_info_collector_mac.mm
new file mode 100644
index 0000000..9698351
--- /dev/null
+++ b/gpu/config/gpu_info_collector_mac.mm
@@ -0,0 +1,220 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info_collector.h"
+
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/sys_string_conversions.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_interface.h"
+
+#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
+#import <IOKit/IOKitLib.h>
+
+namespace gpu {
+
+namespace {
+
+const UInt32 kVendorIDIntel = 0x8086;
+const UInt32 kVendorIDNVidia = 0x10de;
+const UInt32 kVendorIDAMD = 0x1002;
+
+// Return 0 if we couldn't find the property.
+// The property values we use should not be 0, so it's OK to use 0 as failure.
+UInt32 GetEntryProperty(io_registry_entry_t entry, CFStringRef property_name) {
+  base::mac::ScopedCFTypeRef<CFDataRef> data_ref(static_cast<CFDataRef>(
+      IORegistryEntrySearchCFProperty(entry,
+                                      kIOServicePlane,
+                                      property_name,
+                                      kCFAllocatorDefault,
+                                      kIORegistryIterateRecursively |
+                                      kIORegistryIterateParents)));
+  if (!data_ref)
+    return 0;
+
+  UInt32 value = 0;
+  const UInt32* value_pointer =
+      reinterpret_cast<const UInt32*>(CFDataGetBytePtr(data_ref));
+  if (value_pointer != NULL)
+    value = *value_pointer;
+  return value;
+}
+
+// Find the info of the current GPU.
+GPUInfo::GPUDevice GetActiveGPU() {
+  GPUInfo::GPUDevice gpu;
+  io_registry_entry_t dsp_port = CGDisplayIOServicePort(kCGDirectMainDisplay);
+  gpu.vendor_id = GetEntryProperty(dsp_port, CFSTR("vendor-id"));
+  gpu.device_id = GetEntryProperty(dsp_port, CFSTR("device-id"));
+  return gpu;
+}
+
+// Scan IO registry for PCI video cards.
+bool CollectPCIVideoCardInfo(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  // Collect all GPUs' info.
+  // match_dictionary will be consumed by IOServiceGetMatchingServices, no need
+  // to release it.
+  CFMutableDictionaryRef match_dictionary = IOServiceMatching("IOPCIDevice");
+  io_iterator_t entry_iterator;
+  std::vector<GPUInfo::GPUDevice> gpu_list;
+  if (IOServiceGetMatchingServices(kIOMasterPortDefault,
+                                   match_dictionary,
+                                   &entry_iterator) == kIOReturnSuccess) {
+    io_registry_entry_t entry;
+    while ((entry = IOIteratorNext(entry_iterator))) {
+      GPUInfo::GPUDevice gpu;
+      if (GetEntryProperty(entry, CFSTR("class-code")) != 0x30000) {
+        // 0x30000 : DISPLAY_VGA
+        continue;
+      }
+      gpu.vendor_id = GetEntryProperty(entry, CFSTR("vendor-id"));
+      gpu.device_id = GetEntryProperty(entry, CFSTR("device-id"));
+      if (gpu.vendor_id && gpu.device_id)
+        gpu_list.push_back(gpu);
+    }
+    IOObjectRelease(entry_iterator);
+  }
+
+  switch (gpu_list.size()) {
+    case 0:
+      return false;
+    case 1:
+      gpu_info->gpu = gpu_list[0];
+      break;
+    case 2:
+      {
+        int integrated = -1;
+        int discrete = -1;
+        if (gpu_list[0].vendor_id == kVendorIDIntel)
+          integrated = 0;
+        else if (gpu_list[1].vendor_id == kVendorIDIntel)
+          integrated = 1;
+        if (integrated >= 0) {
+          switch (gpu_list[1 - integrated].vendor_id) {
+            case kVendorIDAMD:
+              gpu_info->amd_switchable = true;
+              discrete = 1 - integrated;
+              break;
+            case kVendorIDNVidia:
+              gpu_info->optimus = true;
+              discrete = 1 - integrated;
+              break;
+            default:
+              break;
+          }
+        }
+        if (integrated >= 0 && discrete >= 0) {
+          // We always put discrete GPU as primary for blacklisting purpose.
+          gpu_info->gpu = gpu_list[discrete];
+          gpu_info->secondary_gpus.push_back(gpu_list[integrated]);
+          break;
+        }
+        // If it's not optimus or amd_switchable, we put the current GPU as
+        // primary.  Fall through to default.
+      }
+    default:
+      {
+        GPUInfo::GPUDevice active_gpu = GetActiveGPU();
+        size_t current = gpu_list.size();
+        if (active_gpu.vendor_id && active_gpu.device_id) {
+          for (size_t i = 0; i < gpu_list.size(); ++i) {
+            if (gpu_list[i].vendor_id == active_gpu.vendor_id &&
+                gpu_list[i].device_id == active_gpu.device_id) {
+              current = i;
+              break;
+            }
+          }
+        }
+        if (current == gpu_list.size()) {
+          // If we fail to identify the current GPU, select any one as primary.
+          current = 0;
+        }
+        for (size_t i = 0; i < gpu_list.size(); ++i) {
+          if (i == current)
+            gpu_info->gpu = gpu_list[i];
+          else
+            gpu_info->secondary_gpus.push_back(gpu_list[i]);
+        }
+      }
+      break;
+  }
+  return (gpu_info->gpu.vendor_id && gpu_info->gpu.device_id);
+}
+
+}  // namespace anonymous
+
+bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo");
+
+  gpu_info->can_lose_context =
+      (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
+  gpu_info->finalized = true;
+  return CollectGraphicsInfoGL(gpu_info);
+}
+
+GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
+  DCHECK(vendor_id && device_id);
+  *vendor_id = 0;
+  *device_id = 0;
+
+  GPUInfo gpu_info;
+  if (CollectPCIVideoCardInfo(&gpu_info)) {
+    *vendor_id = gpu_info.gpu.vendor_id;
+    *device_id = gpu_info.gpu.device_id;
+    return kGpuIDSuccess;
+  }
+  return kGpuIDFailure;
+}
+
+bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  std::string model_name;
+  int32 model_major = 0, model_minor = 0;
+  base::mac::ParseModelIdentifier(base::mac::GetModelIdentifier(),
+                                  &model_name, &model_major, &model_minor);
+  ReplaceChars(model_name, " ", "_", &gpu_info->machine_model);
+  gpu_info->machine_model += " " + base::IntToString(model_major) +
+                             "." + base::IntToString(model_minor);
+
+  return CollectPCIVideoCardInfo(gpu_info);
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  // Extract the OpenGL driver version string from the GL_VERSION string.
+  // Mac OpenGL drivers have the driver version
+  // at the end of the gl version string preceded by a dash.
+  // Use some jiggery-pokery to turn that utf8 string into a std::wstring.
+  std::string gl_version_string = gpu_info->gl_version_string;
+  size_t pos = gl_version_string.find_last_of('-');
+  if (pos == std::string::npos)
+    return false;
+  gpu_info->driver_version = gl_version_string.substr(pos + 1);
+  return true;
+}
+
+void MergeGPUInfo(GPUInfo* basic_gpu_info,
+                  const GPUInfo& context_gpu_info) {
+  MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
+}
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_info_collector_ozone.cc b/gpu/config/gpu_info_collector_ozone.cc
new file mode 100644
index 0000000..acef6ae
--- /dev/null
+++ b/gpu/config/gpu_info_collector_ozone.cc
@@ -0,0 +1,37 @@
+// Copyright 2013 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 "gpu/config/gpu_info_collector.h"
+
+#include "base/logging.h"
+
+namespace gpu {
+
+bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
+  return CollectBasicGraphicsInfo(gpu_info);
+}
+
+GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
+  DCHECK(vendor_id && device_id);
+  *vendor_id = 0;
+  *device_id = 0;
+  return kGpuIDNotSupported;
+}
+
+bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
+  gpu_info->can_lose_context = false;
+  return true;
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void MergeGPUInfo(GPUInfo* basic_gpu_info,
+                  const GPUInfo& context_gpu_info) {
+  MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
+}
+
+}  // namespace gpu_info_collector
diff --git a/gpu/config/gpu_info_collector_unittest.cc b/gpu/config/gpu_info_collector_unittest.cc
new file mode 100644
index 0000000..5e61ee2
--- /dev/null
+++ b/gpu/config/gpu_info_collector_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2012 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/scoped_ptr.h"
+#include "gpu/config/gpu_info.h"
+#include "gpu/config/gpu_info_collector.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_mock.h"
+
+using ::gfx::MockGLInterface;
+using ::testing::Return;
+
+namespace gpu {
+
+class GPUInfoCollectorTest : public testing::Test {
+ public:
+  GPUInfoCollectorTest() {}
+  virtual ~GPUInfoCollectorTest() { }
+
+  virtual void SetUp() {
+    // TODO(kbr): make this setup robust in the case where
+    // GLSurface::InitializeOneOff() has already been called by
+    // another unit test. http://crbug.com/100285
+    gfx::InitializeGLBindings(gfx::kGLImplementationMockGL);
+    gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>());
+    ::gfx::GLInterface::SetGLInterface(gl_.get());
+#if defined(OS_WIN)
+    const uint32 vendor_id = 0x10de;
+    const uint32 device_id = 0x0658;
+    const char* driver_vendor = "";  // not implemented
+    const char* driver_version = "";
+    const char* shader_version = "1.40";
+    const char* gl_version = "3.1";
+    const char* gl_renderer = "Quadro FX 380/PCI/SSE2";
+    const char* gl_vendor = "NVIDIA Corporation";
+    const char* gl_version_string = "3.1.0";
+    const char* gl_shading_language_version = "1.40 NVIDIA via Cg compiler";
+    const char* gl_extensions =
+        "GL_OES_packed_depth_stencil GL_EXT_texture_format_BGRA8888 "
+        "GL_EXT_read_format_bgra";
+#elif defined(OS_MACOSX)
+    const uint32 vendor_id = 0x10de;
+    const uint32 device_id = 0x0640;
+    const char* driver_vendor = "";  // not implemented
+    const char* driver_version = "1.6.18";
+    const char* shader_version = "1.20";
+    const char* gl_version = "2.1";
+    const char* gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
+    const char* gl_vendor = "NVIDIA Corporation";
+    const char* gl_version_string = "2.1 NVIDIA-1.6.18";
+    const char* gl_shading_language_version = "1.20 ";
+    const char* gl_extensions =
+        "GL_OES_packed_depth_stencil GL_EXT_texture_format_BGRA8888 "
+        "GL_EXT_read_format_bgra";
+#else  // defined (OS_LINUX)
+    const uint32 vendor_id = 0x10de;
+    const uint32 device_id = 0x0658;
+    const char* driver_vendor = "NVIDIA";
+    const char* driver_version = "195.36.24";
+    const char* shader_version = "1.50";
+    const char* gl_version = "3.2";
+    const char* gl_renderer = "Quadro FX 380/PCI/SSE2";
+    const char* gl_vendor = "NVIDIA Corporation";
+    const char* gl_version_string = "3.2.0 NVIDIA 195.36.24";
+    const char* gl_shading_language_version = "1.50 NVIDIA via Cg compiler";
+    const char* gl_extensions =
+        "GL_OES_packed_depth_stencil GL_EXT_texture_format_BGRA8888 "
+        "GL_EXT_read_format_bgra";
+#endif
+    test_values_.gpu.vendor_id = vendor_id;
+    test_values_.gpu.device_id = device_id;
+    test_values_.driver_vendor = driver_vendor;
+    test_values_.driver_version =driver_version;
+    test_values_.pixel_shader_version = shader_version;
+    test_values_.vertex_shader_version = shader_version;
+    test_values_.gl_version = gl_version;
+    test_values_.gl_renderer = gl_renderer;
+    test_values_.gl_vendor = gl_vendor;
+    test_values_.gl_version_string = gl_version_string;
+    test_values_.gl_extensions = gl_extensions;
+    test_values_.can_lose_context = false;
+
+    EXPECT_CALL(*gl_, GetString(GL_EXTENSIONS))
+        .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+            gl_extensions)));
+    EXPECT_CALL(*gl_, GetString(GL_SHADING_LANGUAGE_VERSION))
+        .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+            gl_shading_language_version)));
+    EXPECT_CALL(*gl_, GetString(GL_VERSION))
+        .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+            gl_version_string)));
+    EXPECT_CALL(*gl_, GetString(GL_VENDOR))
+        .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+            gl_vendor)));
+    EXPECT_CALL(*gl_, GetString(GL_RENDERER))
+        .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+            gl_renderer)));
+  }
+
+  virtual void TearDown() {
+    ::gfx::GLInterface::SetGLInterface(NULL);
+    gl_.reset();
+  }
+
+ public:
+  // Use StrictMock to make 100% sure we know how GL will be called.
+  scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_;
+  GPUInfo test_values_;
+};
+
+// TODO(rlp): Test the vendor and device id collection if deemed necessary as
+//            it involves several complicated mocks for each platform.
+
+// TODO(kbr): re-enable these tests; see http://crbug.com/100285 .
+
+TEST_F(GPUInfoCollectorTest, DISABLED_DriverVendorGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.driver_vendor,
+            gpu_info.driver_vendor);
+}
+
+// Skip Windows because the driver version is obtained from bot registry.
+#if !defined(OS_WIN)
+TEST_F(GPUInfoCollectorTest, DISABLED_DriverVersionGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.driver_version,
+            gpu_info.driver_version);
+}
+#endif
+
+TEST_F(GPUInfoCollectorTest, DISABLED_PixelShaderVersionGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.pixel_shader_version,
+            gpu_info.pixel_shader_version);
+}
+
+TEST_F(GPUInfoCollectorTest, DISABLED_VertexShaderVersionGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.vertex_shader_version,
+            gpu_info.vertex_shader_version);
+}
+
+TEST_F(GPUInfoCollectorTest, DISABLED_GLVersionGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.gl_version,
+            gpu_info.gl_version);
+}
+
+TEST_F(GPUInfoCollectorTest, DISABLED_GLVersionStringGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.gl_version_string,
+            gpu_info.gl_version_string);
+}
+
+TEST_F(GPUInfoCollectorTest, DISABLED_GLRendererGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.gl_renderer,
+            gpu_info.gl_renderer);
+}
+
+TEST_F(GPUInfoCollectorTest, DISABLED_GLVendorGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.gl_vendor,
+            gpu_info.gl_vendor);
+}
+
+TEST_F(GPUInfoCollectorTest, DISABLED_GLExtensionsGL) {
+  GPUInfo gpu_info;
+  CollectGraphicsInfoGL(&gpu_info);
+  EXPECT_EQ(test_values_.gl_extensions,
+            gpu_info.gl_extensions);
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc
new file mode 100644
index 0000000..de96450
--- /dev/null
+++ b/gpu/config/gpu_info_collector_win.cc
@@ -0,0 +1,654 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info_collector.h"
+
+// This has to be included before windows.h.
+#include "third_party/re2/re2/re2.h"
+
+#include <windows.h>
+#include <d3d9.h>
+#include <d3d11.h>
+#include <dxgi.h>
+#include <setupapi.h>
+
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/scoped_native_library.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/string16.h"
+#include "base/stringprintf.h"
+#include "base/threading/thread.h"
+#include "base/threading/worker_pool.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_com_initializer.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/windows_version.h"
+#include "third_party/libxml/chromium/libxml_utils.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_surface_egl.h"
+
+namespace gpu {
+
+namespace {
+
+// This must be kept in sync with histograms.xml.
+enum DisplayLinkInstallationStatus {
+  DISPLAY_LINK_NOT_INSTALLED,
+  DISPLAY_LINK_7_1_OR_EARLIER,
+  DISPLAY_LINK_7_2_OR_LATER,
+  DISPLAY_LINK_INSTALLATION_STATUS_MAX
+};
+
+float ReadXMLFloatValue(XmlReader* reader) {
+  std::string score_string;
+  if (!reader->ReadElementContent(&score_string))
+    return 0.0;
+
+  double score;
+  if (!base::StringToDouble(score_string, &score))
+    return 0.0;
+
+  return static_cast<float>(score);
+}
+
+GpuPerformanceStats RetrieveGpuPerformanceStats() {
+  TRACE_EVENT0("gpu", "RetrieveGpuPerformanceStats");
+
+  // If the user re-runs the assessment without restarting, the COM API
+  // returns WINSAT_ASSESSMENT_STATE_NOT_AVAILABLE. Because of that and
+  // http://crbug.com/124325, read the assessment result files directly.
+  GpuPerformanceStats stats;
+
+  // Get path to WinSAT results files.
+  wchar_t winsat_results_path[MAX_PATH];
+  DWORD size = ExpandEnvironmentStrings(
+      L"%WinDir%\\Performance\\WinSAT\\DataStore\\",
+      winsat_results_path, MAX_PATH);
+  if (size == 0 || size > MAX_PATH) {
+    LOG(ERROR) << "The path to the WinSAT results is too long: "
+               << size << " chars.";
+    return stats;
+  }
+
+  // Find most recent formal assessment results.
+  file_util::FileEnumerator file_enumerator(
+      base::FilePath(winsat_results_path),
+      false,  // not recursive
+      file_util::FileEnumerator::FILES,
+      FILE_PATH_LITERAL("* * Formal.Assessment (*).WinSAT.xml"));
+
+  base::FilePath current_results;
+  for (base::FilePath results = file_enumerator.Next(); !results.empty();
+       results = file_enumerator.Next()) {
+    // The filenames start with the date and time as yyyy-mm-dd hh.mm.ss.xxx,
+    // so the greatest file lexicographically is also the most recent file.
+    if (base::FilePath::CompareLessIgnoreCase(current_results.value(),
+                                              results.value()))
+      current_results = results;
+  }
+
+  std::string current_results_string = current_results.MaybeAsASCII();
+  if (current_results_string.empty()) {
+    LOG(ERROR) << "Can't retrieve a valid WinSAT assessment.";
+    return stats;
+  }
+
+  // Get relevant scores from results file. XML schema at:
+  // http://msdn.microsoft.com/en-us/library/windows/desktop/aa969210.aspx
+  XmlReader reader;
+  if (!reader.LoadFile(current_results_string)) {
+    LOG(ERROR) << "Could not open WinSAT results file.";
+    return stats;
+  }
+  // Descend into <WinSAT> root element.
+  if (!reader.SkipToElement() || !reader.Read()) {
+    LOG(ERROR) << "Could not read WinSAT results file.";
+    return stats;
+  }
+
+  // Search for <WinSPR> element containing the results.
+  do {
+    if (reader.NodeName() == "WinSPR")
+      break;
+  } while (reader.Next());
+  // Descend into <WinSPR> element.
+  if (!reader.Read()) {
+    LOG(ERROR) << "Could not find WinSPR element in results file.";
+    return stats;
+  }
+
+  // Read scores.
+  for (int depth = reader.Depth(); reader.Depth() == depth; reader.Next()) {
+    std::string node_name = reader.NodeName();
+    if (node_name == "SystemScore")
+      stats.overall = ReadXMLFloatValue(&reader);
+    else if (node_name == "GraphicsScore")
+      stats.graphics = ReadXMLFloatValue(&reader);
+    else if (node_name == "GamingScore")
+      stats.gaming = ReadXMLFloatValue(&reader);
+  }
+
+  if (stats.overall == 0.0)
+    LOG(ERROR) << "Could not read overall score from assessment results.";
+  if (stats.graphics == 0.0)
+    LOG(ERROR) << "Could not read graphics score from assessment results.";
+  if (stats.gaming == 0.0)
+    LOG(ERROR) << "Could not read gaming score from assessment results.";
+
+  return stats;
+}
+
+GpuPerformanceStats RetrieveGpuPerformanceStatsWithHistograms() {
+  base::TimeTicks start_time = base::TimeTicks::Now();
+
+  GpuPerformanceStats stats = RetrieveGpuPerformanceStats();
+
+  UMA_HISTOGRAM_TIMES("GPU.WinSAT.ReadResultsFileTime",
+                      base::TimeTicks::Now() - start_time);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.OverallScore2",
+                              stats.overall * 10, 10, 200, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GraphicsScore2",
+                              stats.graphics * 10, 10, 200, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GamingScore2",
+                              stats.gaming * 10, 10, 200, 50);
+  UMA_HISTOGRAM_BOOLEAN(
+      "GPU.WinSAT.HasResults",
+      stats.overall != 0.0 && stats.graphics != 0.0 && stats.gaming != 0.0);
+
+  return stats;
+}
+
+// Returns the display link driver version or an invalid version if it is
+// not installed.
+Version DisplayLinkVersion() {
+  base::win::RegKey key;
+
+  if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
+    return Version();
+
+  if (key.OpenKey(L"DisplayLink", KEY_READ | KEY_WOW64_64KEY))
+    return Version();
+
+  if (key.OpenKey(L"Core", KEY_READ | KEY_WOW64_64KEY))
+    return Version();
+
+  string16 version;
+  if (key.ReadValue(L"Version", &version))
+    return Version();
+
+  return Version(WideToASCII(version));
+}
+
+// Returns whether Lenovo dCute is installed.
+bool IsLenovoDCuteInstalled() {
+  base::win::RegKey key;
+
+  if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
+    return false;
+
+  if (key.OpenKey(L"Lenovo", KEY_READ | KEY_WOW64_64KEY))
+    return false;
+
+  if (key.OpenKey(L"Lenovo dCute", KEY_READ | KEY_WOW64_64KEY))
+    return false;
+
+  return true;
+}
+
+// Determines whether D3D11 won't work, either because it is not supported on
+// the machine or because it is known it is likely to crash.
+bool D3D11ShouldWork(const GPUInfo& gpu_info) {
+  // Windows XP never supports D3D11.
+  if (base::win::GetVersion() <= base::win::VERSION_XP)
+    return false;
+
+  // Intel?
+  if (gpu_info.gpu.vendor_id == 0x8086) {
+    // 2nd Generation Core Processor Family Integrated Graphics Controller
+    // or Intel Ivy Bridge?
+    if (gpu_info.gpu.device_id == 0x0102 ||
+        gpu_info.gpu.device_id == 0x0106 ||
+        gpu_info.gpu.device_id == 0x0116 ||
+        gpu_info.gpu.device_id == 0x0126 ||
+        gpu_info.gpu.device_id == 0x0152 ||
+        gpu_info.gpu.device_id == 0x0156 ||
+        gpu_info.gpu.device_id == 0x015a ||
+        gpu_info.gpu.device_id == 0x0162 ||
+        gpu_info.gpu.device_id == 0x0166) {
+      // http://crbug.com/196373.
+      if (base::win::GetVersion() == base::win::VERSION_VISTA)
+        return false;
+
+      // http://crbug.com/175525.
+      if (gpu_info.display_link_version.IsValid())
+        return false;
+    }
+  }
+
+  return true;
+}
+
+// Collects information about the level of D3D11 support and records it in
+// the UMA stats. Records no stats when D3D11 in not supported at all.
+void CollectD3D11SupportOnWorkerThread() {
+  TRACE_EVENT0("gpu", "CollectD3D11Support");
+
+  typedef HRESULT (WINAPI *D3D11CreateDeviceFunc)(
+      IDXGIAdapter* adapter,
+      D3D_DRIVER_TYPE driver_type,
+      HMODULE software,
+      UINT flags,
+      const D3D_FEATURE_LEVEL* feature_levels,
+      UINT num_feature_levels,
+      UINT sdk_version,
+      ID3D11Device** device,
+      D3D_FEATURE_LEVEL* feature_level,
+      ID3D11DeviceContext** immediate_context);
+
+  // This enumeration must be kept in sync with histograms.xml. Do not reorder
+  // the members; always add to the end.
+  enum FeatureLevel {
+    FEATURE_LEVEL_UNKNOWN,
+    FEATURE_LEVEL_NO_D3D11_DLL,
+    FEATURE_LEVEL_NO_CREATE_DEVICE_ENTRY_POINT,
+    FEATURE_LEVEL_DEVICE_CREATION_FAILED,
+    FEATURE_LEVEL_9_1,
+    FEATURE_LEVEL_9_2,
+    FEATURE_LEVEL_9_3,
+    FEATURE_LEVEL_10_0,
+    FEATURE_LEVEL_10_1,
+    FEATURE_LEVEL_11_0,
+    NUM_FEATURE_LEVELS
+  };
+
+  FeatureLevel feature_level = FEATURE_LEVEL_UNKNOWN;
+  UINT bgra_support = 0;
+
+  // This module is leaked in case it is hooked by third party software.
+  base::NativeLibrary d3d11_module = base::LoadNativeLibrary(
+      base::FilePath(L"d3d11.dll"),
+      NULL);
+
+  if (!d3d11_module) {
+    feature_level = FEATURE_LEVEL_NO_D3D11_DLL;
+  } else {
+    D3D11CreateDeviceFunc create_func =
+        reinterpret_cast<D3D11CreateDeviceFunc>(
+            base::GetFunctionPointerFromNativeLibrary(d3d11_module,
+                                                      "D3D11CreateDevice"));
+    if (!create_func) {
+      feature_level = FEATURE_LEVEL_NO_CREATE_DEVICE_ENTRY_POINT;
+    } else {
+      static const D3D_FEATURE_LEVEL d3d_feature_levels[] = {
+        D3D_FEATURE_LEVEL_11_0,
+        D3D_FEATURE_LEVEL_10_1,
+        D3D_FEATURE_LEVEL_10_0,
+        D3D_FEATURE_LEVEL_9_3,
+        D3D_FEATURE_LEVEL_9_2,
+        D3D_FEATURE_LEVEL_9_1
+      };
+
+      base::win::ScopedComPtr<ID3D11Device> device;
+      D3D_FEATURE_LEVEL d3d_feature_level;
+      base::win::ScopedComPtr<ID3D11DeviceContext> device_context;
+      HRESULT hr = create_func(NULL,
+                               D3D_DRIVER_TYPE_HARDWARE,
+                               NULL,
+                               0,
+                               d3d_feature_levels,
+                               arraysize(d3d_feature_levels),
+                               D3D11_SDK_VERSION,
+                               device.Receive(),
+                               &d3d_feature_level,
+                               device_context.Receive());
+      if (FAILED(hr)) {
+        feature_level = FEATURE_LEVEL_DEVICE_CREATION_FAILED;
+      } else {
+        switch (d3d_feature_level) {
+          case D3D_FEATURE_LEVEL_11_0:
+            feature_level = FEATURE_LEVEL_11_0;
+            break;
+          case D3D_FEATURE_LEVEL_10_1:
+            feature_level = FEATURE_LEVEL_10_1;
+            break;
+          case D3D_FEATURE_LEVEL_10_0:
+            feature_level = FEATURE_LEVEL_10_0;
+            break;
+          case D3D_FEATURE_LEVEL_9_3:
+            feature_level = FEATURE_LEVEL_9_3;
+            break;
+          case D3D_FEATURE_LEVEL_9_2:
+            feature_level = FEATURE_LEVEL_9_2;
+            break;
+          case D3D_FEATURE_LEVEL_9_1:
+            feature_level = FEATURE_LEVEL_9_1;
+            break;
+          default:
+            NOTREACHED();
+            break;
+        }
+
+        hr = device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM,
+                                        &bgra_support);
+        DCHECK(SUCCEEDED(hr));
+      }
+    }
+  }
+
+  UMA_HISTOGRAM_ENUMERATION("GPU.D3D11_FeatureLevel",
+                            feature_level,
+                            NUM_FEATURE_LEVELS);
+
+  // ANGLE requires at least feature level 10.0. Do not record any further
+  // stats if ANGLE would not work anyway.
+  if (feature_level < FEATURE_LEVEL_10_0)
+    return;
+
+  UMA_HISTOGRAM_BOOLEAN(
+      "GPU.D3D11_B8G8R8A8_Texture2DSupport",
+      (bgra_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0);
+  UMA_HISTOGRAM_BOOLEAN(
+      "GPU.D3D11_B8G8R8A8_RenderTargetSupport",
+      (bgra_support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) != 0);
+}
+
+// Collects information about the level of D3D11 support and records it in
+// the UMA stats. Records no stats when D3D11 in not supported at all.
+void CollectD3D11Support() {
+  // D3D11 takes about 50ms to initialize so do this on a worker thread.
+  base::WorkerPool::PostTask(
+      FROM_HERE,
+      base::Bind(CollectD3D11SupportOnWorkerThread),
+      false);
+}
+}  // namespace anonymous
+
+#if !defined(GOOGLE_CHROME_BUILD)
+AMDVideoCardType GetAMDVideocardType() {
+  return STANDALONE;
+}
+#else
+// This function has a real implementation for official builds that can
+// be found in src/third_party/amd.
+AMDVideoCardType GetAMDVideocardType();
+#endif
+
+bool CollectDriverInfoD3D(const std::wstring& device_id,
+                          GPUInfo* gpu_info) {
+  TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
+
+  // create device info for the display device
+  HDEVINFO device_info = SetupDiGetClassDevsW(
+      NULL, device_id.c_str(), NULL,
+      DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
+  if (device_info == INVALID_HANDLE_VALUE) {
+    LOG(ERROR) << "Creating device info failed";
+    return false;
+  }
+
+  DWORD index = 0;
+  bool found = false;
+  SP_DEVINFO_DATA device_info_data;
+  device_info_data.cbSize = sizeof(device_info_data);
+  while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) {
+    WCHAR value[255];
+    if (SetupDiGetDeviceRegistryPropertyW(device_info,
+                                        &device_info_data,
+                                        SPDRP_DRIVER,
+                                        NULL,
+                                        reinterpret_cast<PBYTE>(value),
+                                        sizeof(value),
+                                        NULL)) {
+      HKEY key;
+      std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\";
+      driver_key += value;
+      LONG result = RegOpenKeyExW(
+          HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key);
+      if (result == ERROR_SUCCESS) {
+        DWORD dwcb_data = sizeof(value);
+        std::string driver_version;
+        result = RegQueryValueExW(
+            key, L"DriverVersion", NULL, NULL,
+            reinterpret_cast<LPBYTE>(value), &dwcb_data);
+        if (result == ERROR_SUCCESS)
+          driver_version = WideToASCII(std::wstring(value));
+
+        std::string driver_date;
+        dwcb_data = sizeof(value);
+        result = RegQueryValueExW(
+            key, L"DriverDate", NULL, NULL,
+            reinterpret_cast<LPBYTE>(value), &dwcb_data);
+        if (result == ERROR_SUCCESS)
+          driver_date = WideToASCII(std::wstring(value));
+
+        std::string driver_vendor;
+        dwcb_data = sizeof(value);
+        result = RegQueryValueExW(
+            key, L"ProviderName", NULL, NULL,
+            reinterpret_cast<LPBYTE>(value), &dwcb_data);
+        if (result == ERROR_SUCCESS) {
+          driver_vendor = WideToASCII(std::wstring(value));
+          if (driver_vendor == "Advanced Micro Devices, Inc." ||
+              driver_vendor == "ATI Technologies Inc.") {
+            // We are conservative and assume that in the absence of a clear
+            // signal the videocard is assumed to be switchable. Additionally,
+            // some switchable systems with Intel GPUs aren't correctly
+            // detected, so always count them.
+            AMDVideoCardType amd_card_type = GetAMDVideocardType();
+            gpu_info->amd_switchable = (gpu_info->gpu.vendor_id == 0x8086) ||
+                                       (amd_card_type != STANDALONE);
+          }
+        }
+
+        gpu_info->driver_vendor = driver_vendor;
+        gpu_info->driver_version = driver_version;
+        gpu_info->driver_date = driver_date;
+        found = true;
+        RegCloseKey(key);
+        break;
+      }
+    }
+  }
+  SetupDiDestroyDeviceInfoList(device_info);
+  return found;
+}
+
+bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
+  TRACE_EVENT0("gpu", "CollectGraphicsInfo");
+
+  DCHECK(gpu_info);
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) {
+    std::string requested_implementation_name =
+        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL);
+    if (requested_implementation_name == "swiftshader") {
+      gpu_info->software_rendering = true;
+      return false;
+    }
+  }
+
+  if (!CollectGraphicsInfoGL(gpu_info))
+    return false;
+
+  // ANGLE's renderer strings are of the form:
+  // ANGLE (<adapter_identifier> Direct3D<version> vs_x_x ps_x_x)
+  std::string direct3d_version;
+  int vertex_shader_major_version = 0;
+  int vertex_shader_minor_version = 0;
+  int pixel_shader_major_version = 0;
+  int pixel_shader_minor_version = 0;
+  gpu_info->adapter_luid = 0;
+  if (RE2::FullMatch(gpu_info->gl_renderer,
+                     "ANGLE \\(.*\\)") &&
+      RE2::PartialMatch(gpu_info->gl_renderer,
+                        " Direct3D(\\w+)",
+                        &direct3d_version) &&
+      RE2::PartialMatch(gpu_info->gl_renderer,
+                        " vs_(\\d+)_(\\d+)",
+                        &vertex_shader_major_version,
+                        &vertex_shader_minor_version) &&
+      RE2::PartialMatch(gpu_info->gl_renderer,
+                        " ps_(\\d+)_(\\d+)",
+                        &pixel_shader_major_version,
+                        &pixel_shader_minor_version)) {
+    gpu_info->can_lose_context = direct3d_version == "9";
+    gpu_info->vertex_shader_version =
+        base::StringPrintf("%d.%d",
+                           vertex_shader_major_version,
+                           vertex_shader_minor_version);
+    gpu_info->pixel_shader_version =
+        base::StringPrintf("%d.%d",
+                           pixel_shader_major_version,
+                           pixel_shader_minor_version);
+
+    // ANGLE's EGL vendor strings are of the form:
+    // Google, Inc. (adapter LUID: 0123456789ABCDEF)
+    // The LUID is optional and identifies the GPU adapter ANGLE is using.
+    const char* egl_vendor = eglQueryString(
+        gfx::GLSurfaceEGL::GetHardwareDisplay(),
+        EGL_VENDOR);
+    RE2::PartialMatch(egl_vendor,
+                      " \\(adapter LUID: ([0-9A-Fa-f]{16})\\)",
+                      RE2::Hex(&gpu_info->adapter_luid));
+
+    // DirectX diagnostics are collected asynchronously because it takes a
+    // couple of seconds. Do not mark gpu_info as complete until that is done.
+    gpu_info->finalized = false;
+  } else {
+    gpu_info->finalized = true;
+  }
+
+  return true;
+}
+
+GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
+  DCHECK(vendor_id && device_id);
+  *vendor_id = 0;
+  *device_id = 0;
+
+  // Taken from http://developer.nvidia.com/object/device_ids.html
+  DISPLAY_DEVICE dd;
+  dd.cb = sizeof(DISPLAY_DEVICE);
+  std::wstring id;
+  for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
+    if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
+      id = dd.DeviceID;
+      break;
+    }
+  }
+
+  if (id.length() > 20) {
+    int vendor = 0, device = 0;
+    std::wstring vendor_string = id.substr(8, 4);
+    std::wstring device_string = id.substr(17, 4);
+    base::HexStringToInt(WideToASCII(vendor_string), &vendor);
+    base::HexStringToInt(WideToASCII(device_string), &device);
+    *vendor_id = vendor;
+    *device_id = device;
+    return kGpuIDSuccess;
+  }
+  return kGpuIDFailure;
+}
+
+bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
+  TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
+
+  DCHECK(gpu_info);
+
+  gpu_info->performance_stats = RetrieveGpuPerformanceStatsWithHistograms();
+
+  // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
+  HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
+  gpu_info->optimus = nvd3d9wrap != NULL;
+
+  gpu_info->lenovo_dcute = IsLenovoDCuteInstalled();
+
+  gpu_info->display_link_version = DisplayLinkVersion();
+
+  if (!gpu_info->display_link_version .IsValid()) {
+    UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
+                              DISPLAY_LINK_NOT_INSTALLED,
+                              DISPLAY_LINK_INSTALLATION_STATUS_MAX);
+  } else if (gpu_info->display_link_version.IsOlderThan("7.2")) {
+    UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
+                              DISPLAY_LINK_7_1_OR_EARLIER,
+                              DISPLAY_LINK_INSTALLATION_STATUS_MAX);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
+                              DISPLAY_LINK_7_2_OR_LATER,
+                              DISPLAY_LINK_INSTALLATION_STATUS_MAX);
+  }
+
+  // Taken from http://developer.nvidia.com/object/device_ids.html
+  DISPLAY_DEVICE dd;
+  dd.cb = sizeof(DISPLAY_DEVICE);
+  std::wstring id;
+  for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
+    if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
+      id = dd.DeviceID;
+      break;
+    }
+  }
+
+  if (id.length() <= 20)
+    return false;
+
+  int vendor_id = 0, device_id = 0;
+  string16 vendor_id_string = id.substr(8, 4);
+  string16 device_id_string = id.substr(17, 4);
+  base::HexStringToInt(WideToASCII(vendor_id_string), &vendor_id);
+  base::HexStringToInt(WideToASCII(device_id_string), &device_id);
+  gpu_info->gpu.vendor_id = vendor_id;
+  gpu_info->gpu.device_id = device_id;
+  // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
+  if (!CollectDriverInfoD3D(id, gpu_info))
+    return false;
+
+  // Collect basic information about supported D3D11 features. Delay for 45
+  // seconds so as not to regress performance tests.
+  if (D3D11ShouldWork(*gpu_info)) {
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&CollectD3D11Support),
+        base::TimeDelta::FromSeconds(45));
+  }
+
+  return true;
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+  TRACE_EVENT0("gpu", "CollectDriverInfoGL");
+
+  if (!gpu_info->driver_version.empty())
+    return true;
+
+  std::string gl_version_string = gpu_info->gl_version_string;
+
+  return RE2::PartialMatch(gl_version_string,
+                           "([\\d\\.]+)$",
+                           &gpu_info->driver_version);
+}
+
+void MergeGPUInfo(GPUInfo* basic_gpu_info,
+                  const GPUInfo& context_gpu_info) {
+  DCHECK(basic_gpu_info);
+
+  if (context_gpu_info.software_rendering) {
+    basic_gpu_info->software_rendering = true;
+    return;
+  }
+
+  MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
+
+  basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics;
+}
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_info_collector_x11.cc b/gpu/config/gpu_info_collector_x11.cc
new file mode 100644
index 0000000..6a5de82
--- /dev/null
+++ b/gpu/config/gpu_info_collector_x11.cc
@@ -0,0 +1,274 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info_collector.h"
+
+#include <X11/Xlib.h>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "library_loaders/libpci.h"
+#include "third_party/libXNVCtrl/NVCtrl.h"
+#include "third_party/libXNVCtrl/NVCtrlLib.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_switches.h"
+
+namespace gpu {
+
+namespace {
+
+// This checks if a system supports PCI bus.
+// We check the existence of /sys/bus/pci or /sys/bug/pci_express.
+bool IsPciSupported() {
+  const base::FilePath pci_path("/sys/bus/pci/");
+  const base::FilePath pcie_path("/sys/bus/pci_express/");
+  return (file_util::PathExists(pci_path) ||
+          file_util::PathExists(pcie_path));
+}
+
+// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
+// Return empty string on failing.
+std::string CollectDriverVersionATI() {
+  const base::FilePath::CharType kATIFileName[] =
+      FILE_PATH_LITERAL("/etc/ati/amdpcsdb.default");
+  base::FilePath ati_file_path(kATIFileName);
+  if (!file_util::PathExists(ati_file_path))
+    return std::string();
+  std::string contents;
+  if (!file_util::ReadFileToString(ati_file_path, &contents))
+    return std::string();
+  base::StringTokenizer t(contents, "\r\n");
+  while (t.GetNext()) {
+    std::string line = t.token();
+    if (StartsWithASCII(line, "ReleaseVersion=", true)) {
+      size_t begin = line.find_first_of("0123456789");
+      if (begin != std::string::npos) {
+        size_t end = line.find_first_not_of("0123456789.", begin);
+        if (end == std::string::npos)
+          return line.substr(begin);
+        else
+          return line.substr(begin, end - begin);
+      }
+    }
+  }
+  return std::string();
+}
+
+// Use NVCtrl extention to query NV driver version.
+// Return empty string on failing.
+std::string CollectDriverVersionNVidia() {
+  Display* display = base::MessagePumpForUI::GetDefaultXDisplay();
+  if (!display) {
+    LOG(ERROR) << "XOpenDisplay failed.";
+    return std::string();
+  }
+  int event_base = 0, error_base = 0;
+  if (!XNVCTRLQueryExtension(display, &event_base, &error_base)) {
+    LOG(INFO) << "NVCtrl extension does not exist.";
+    return std::string();
+  }
+  int screen_count = ScreenCount(display);
+  for (int screen = 0; screen < screen_count; ++screen) {
+    char* buffer = NULL;
+    if (XNVCTRLIsNvScreen(display, screen) &&
+        XNVCTRLQueryStringAttribute(display, screen, 0,
+                                    NV_CTRL_STRING_NVIDIA_DRIVER_VERSION,
+                                    &buffer)) {
+      std::string driver_version(buffer);
+      XFree(buffer);
+      return driver_version;
+    }
+  }
+  return std::string();
+}
+
+const uint32 kVendorIDIntel = 0x8086;
+const uint32 kVendorIDNVidia = 0x10de;
+const uint32 kVendorIDAMD = 0x1002;
+
+bool CollectPCIVideoCardInfo(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  if (IsPciSupported() == false) {
+    VLOG(1) << "PCI bus scanning is not supported";
+    return false;
+  }
+
+  // TODO(zmo): be more flexible about library name.
+  LibPciLoader libpci_loader;
+  if (!libpci_loader.Load("libpci.so.3") &&
+      !libpci_loader.Load("libpci.so")) {
+    VLOG(1) << "Failed to locate libpci";
+    return false;
+  }
+
+  pci_access* access = (libpci_loader.pci_alloc)();
+  DCHECK(access != NULL);
+  (libpci_loader.pci_init)(access);
+  (libpci_loader.pci_scan_bus)(access);
+  bool primary_gpu_identified = false;
+  for (pci_dev* device = access->devices;
+       device != NULL; device = device->next) {
+    // Fill the IDs and class fields.
+    (libpci_loader.pci_fill_info)(device, 33);
+    // TODO(zmo): there might be other classes that qualify as display devices.
+    if (device->device_class != 0x0300)  // Device class is DISPLAY_VGA.
+      continue;
+
+    GPUInfo::GPUDevice gpu;
+    gpu.vendor_id = device->vendor_id;
+    gpu.device_id = device->device_id;
+
+    if (!primary_gpu_identified) {
+      primary_gpu_identified = true;
+      gpu_info->gpu = gpu;
+    } else {
+      // TODO(zmo): if there are multiple GPUs, we assume the non Intel
+      // one is primary. Revisit this logic because we actually don't know
+      // which GPU we are using at this point.
+      if (gpu_info->gpu.vendor_id == kVendorIDIntel &&
+          gpu.vendor_id != kVendorIDIntel) {
+        gpu_info->secondary_gpus.push_back(gpu_info->gpu);
+        gpu_info->gpu = gpu;
+      } else {
+        gpu_info->secondary_gpus.push_back(gpu);
+      }
+    }
+  }
+
+  // Detect Optimus or AMD Switchable GPU.
+  if (gpu_info->secondary_gpus.size() == 1 &&
+      gpu_info->secondary_gpus[0].vendor_id == kVendorIDIntel) {
+    if (gpu_info->gpu.vendor_id == kVendorIDNVidia)
+      gpu_info->optimus = true;
+    if (gpu_info->gpu.vendor_id == kVendorIDAMD)
+      gpu_info->amd_switchable = true;
+  }
+
+  (libpci_loader.pci_cleanup)(access);
+  return (primary_gpu_identified);
+}
+
+}  // namespace anonymous
+
+bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo");
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kGpuNoContextLost)) {
+    gpu_info->can_lose_context = false;
+  } else {
+#if defined(OS_CHROMEOS)
+    gpu_info->can_lose_context = false;
+#else
+    // TODO(zmo): need to consider the case where we are running on top
+    // of desktop GL and GL_ARB_robustness extension is available.
+    gpu_info->can_lose_context =
+        (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
+#endif
+  }
+
+  gpu_info->finalized = true;
+  bool rt = CollectGraphicsInfoGL(gpu_info);
+
+  return rt;
+}
+
+GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
+  DCHECK(vendor_id && device_id);
+  *vendor_id = 0;
+  *device_id = 0;
+
+  GPUInfo gpu_info;
+  if (CollectPCIVideoCardInfo(&gpu_info)) {
+    *vendor_id = gpu_info.gpu.vendor_id;
+    *device_id = gpu_info.gpu.device_id;
+    return kGpuIDSuccess;
+  }
+  return kGpuIDFailure;
+}
+
+bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  bool rt = CollectPCIVideoCardInfo(gpu_info);
+
+  std::string driver_version;
+  switch (gpu_info->gpu.vendor_id) {
+    case kVendorIDAMD:
+      driver_version = CollectDriverVersionATI();
+      if (!driver_version.empty()) {
+        gpu_info->driver_vendor = "ATI / AMD";
+        gpu_info->driver_version = driver_version;
+      }
+      break;
+    case kVendorIDNVidia:
+      driver_version = CollectDriverVersionNVidia();
+      if (!driver_version.empty()) {
+        gpu_info->driver_vendor = "NVIDIA";
+        gpu_info->driver_version = driver_version;
+      }
+      break;
+    case kVendorIDIntel:
+      // In dual-GPU cases, sometimes PCI scan only gives us the
+      // integrated GPU (i.e., the Intel one).
+      driver_version = CollectDriverVersionNVidia();
+      if (!driver_version.empty()) {
+        gpu_info->driver_vendor = "NVIDIA";
+        gpu_info->driver_version = driver_version;
+        // Machines with more than two GPUs are not handled.
+        if (gpu_info->secondary_gpus.size() <= 1)
+          gpu_info->optimus = true;
+      }
+      break;
+  }
+
+  return rt;
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+  DCHECK(gpu_info);
+
+  std::string gl_version_string = gpu_info->gl_version_string;
+  if (StartsWithASCII(gl_version_string, "OpenGL ES", true))
+    gl_version_string = gl_version_string.substr(10);
+  std::vector<std::string> pieces;
+  base::SplitStringAlongWhitespace(gl_version_string, &pieces);
+  // In linux, the gl version string might be in the format of
+  //   GLVersion DriverVendor DriverVersion
+  if (pieces.size() < 3)
+    return false;
+
+  std::string driver_version = pieces[2];
+  size_t pos = driver_version.find_first_not_of("0123456789.");
+  if (pos == 0)
+    return false;
+  if (pos != std::string::npos)
+    driver_version = driver_version.substr(0, pos);
+
+  gpu_info->driver_vendor = pieces[1];
+  gpu_info->driver_version = driver_version;
+  return true;
+}
+
+void MergeGPUInfo(GPUInfo* basic_gpu_info,
+                  const GPUInfo& context_gpu_info) {
+  MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
+}
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_info_unittest.cc b/gpu/config/gpu_info_unittest.cc
new file mode 100644
index 0000000..f6b6eb6
--- /dev/null
+++ b/gpu/config/gpu_info_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+// Test that an empty GPUInfo has valid members
+TEST(GPUInfoBasicTest, EmptyGPUInfo) {
+  GPUInfo gpu_info;
+  EXPECT_EQ(gpu_info.finalized, false);
+  EXPECT_EQ(gpu_info.initialization_time.ToInternalValue(), 0);
+  EXPECT_EQ(gpu_info.gpu.vendor_id, 0u);
+  EXPECT_EQ(gpu_info.gpu.device_id, 0u);
+  EXPECT_EQ(gpu_info.secondary_gpus.size(), 0u);
+  EXPECT_EQ(gpu_info.driver_vendor, "");
+  EXPECT_EQ(gpu_info.driver_version, "");
+  EXPECT_EQ(gpu_info.driver_date, "");
+  EXPECT_EQ(gpu_info.pixel_shader_version, "");
+  EXPECT_EQ(gpu_info.vertex_shader_version, "");
+  EXPECT_EQ(gpu_info.gl_version, "");
+  EXPECT_EQ(gpu_info.gl_version_string, "");
+  EXPECT_EQ(gpu_info.gl_vendor, "");
+  EXPECT_EQ(gpu_info.gl_renderer, "");
+  EXPECT_EQ(gpu_info.gl_extensions, "");
+  EXPECT_EQ(gpu_info.can_lose_context, false);
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_performance_stats.h b/gpu/config/gpu_performance_stats.h
new file mode 100644
index 0000000..2ade8bf
--- /dev/null
+++ b/gpu/config/gpu_performance_stats.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_PERFORMANCE_STATS_H_
+#define GPU_CONFIG_GPU_PERFORMANCE_STATS_H_
+
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+struct GPU_EXPORT GpuPerformanceStats {
+  GpuPerformanceStats() : graphics(0.f), gaming(0.f), overall(0.f) {}
+
+  float graphics;
+  float gaming;
+  float overall;
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_PERFORMANCE_STATS_H_
+
diff --git a/gpu/config/gpu_switching_list.cc b/gpu/config/gpu_switching_list.cc
new file mode 100644
index 0000000..b87dfb2
--- /dev/null
+++ b/gpu/config/gpu_switching_list.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 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 "gpu/config/gpu_switching_list.h"
+
+#include "gpu/config/gpu_switching_option.h"
+
+namespace gpu {
+
+GpuSwitchingList::GpuSwitchingList()
+    : GpuControlList() {
+}
+
+GpuSwitchingList::~GpuSwitchingList() {
+}
+
+// static
+GpuSwitchingList* GpuSwitchingList::Create() {
+  GpuSwitchingList* list = new GpuSwitchingList();
+  list->AddSupportedFeature("force_integrated",
+                            GPU_SWITCHING_OPTION_FORCE_INTEGRATED);
+  list->AddSupportedFeature("force_discrete",
+                            GPU_SWITCHING_OPTION_FORCE_DISCRETE);
+  return list;
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_switching_list.h b/gpu/config/gpu_switching_list.h
new file mode 100644
index 0000000..095597a
--- /dev/null
+++ b/gpu/config/gpu_switching_list.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 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 GPU_CONFIG_GPU_SWITCHING_LIST_H_
+#define GPU_CONFIG_GPU_SWITCHING_LIST_H_
+
+#include <string>
+
+#include "gpu/config/gpu_control_list.h"
+
+namespace gpu {
+
+class GPU_EXPORT GpuSwitchingList : public GpuControlList {
+ public:
+  virtual ~GpuSwitchingList();
+
+  static GpuSwitchingList* Create();
+
+ private:
+  GpuSwitchingList();
+
+  DISALLOW_COPY_AND_ASSIGN(GpuSwitchingList);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_SWITCHING_LIST_H_
+
diff --git a/gpu/config/gpu_switching_list_json.cc b/gpu/config/gpu_switching_list_json.cc
new file mode 100644
index 0000000..9916b02
--- /dev/null
+++ b/gpu/config/gpu_switching_list_json.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2013 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.
+
+// Determines whether a certain gpu is prefered in a dual-gpu situation.
+// A valid gpu_switching_list.json file are in the format of
+// {
+//   "version": "x.y",
+//   "entries": [
+//     { // entry 1
+//     },
+//     ...
+//     { // entry n
+//     }
+//   ]
+// }
+//
+// Each entry contains the following fields (fields are optional unless
+// specifically described as mandatory below):
+// 1. "id" is an integer.  0 is reserved.  This field is mandatory.
+// 2. "os" contains "type" and an optional "version". "type" could be "macosx",
+//    "linux", "win", "chromeos", or "any".  "any" is the same as not specifying
+//    "os".
+//    "version" is a VERSION structure (defined below).
+// 3. "vendor_id" is a string.  0 is reserved.
+// 4. "device_id" is an array of strings.  0 is reserved.
+// 5. "multi_gpu_style" is a string, valid values include "optimus", and
+//    "amd_switchable".
+// 6. "multi_gpu_category" is a string, valid values include "any", "primary",
+//    and "secondary".  If unspecified, the default value is "primary".
+// 7. "driver_vendor" is a STRING structure (defined below).
+// 8. "driver_version" is a VERSION structure (defined below).
+// 9. "driver_date" is a VERSION structure (defined below).
+//    The version is interpreted as "year.month.day".
+// 10. "gl_vendor" is a STRING structure (defined below).
+// 11. "gl_renderer" is a STRING structure (defined below).
+// 12. "gl_extensions" is a STRING structure (defined below).
+// 13. "perf_graphics" is a FLOAT structure (defined below).
+// 14. "perf_gaming" is a FLOAT structure (defined below).
+// 15. "perf_overall" is a FLOAT structure (defined below).
+// 16. "machine_model" contais "name" and an optional "version".  "name" is a
+//     STRING structure and "version" is a VERSION structure (defined below).
+// 17. "gpu_count" is a INT structure (defined below).
+// 18  "cpu_info" is a STRING structure (defined below).
+// 19. "exceptions" is a list of entries.
+// 20. "features" is a list of gpu switching options, including
+//     "force_discrete" and "force_integrated".
+//     This field is mandatory.
+// 21. "description" has the description of the entry.
+// 22. "webkit_bugs" is an array of associated webkit bug numbers.
+// 23. "cr_bugs" is an array of associated webkit bug numbers.
+// 24. "browser_version" is a VERSION structure (defined below).  If this
+//     condition is not satisfied, the entry will be ignored.  If it is not
+//     present, then the entry applies to all versions of the browser.
+// 25. "disabled" is a boolean. If it is present, the entry will be skipped.
+//     This can not be used in exceptions.
+//
+// VERSION includes "op", "style", "number", and "number2".  "op" can be any of
+// the following values: "=", "<", "<=", ">", ">=", "any", "between".  "style"
+// is optional and can be "lexical" or "numerical"; if it's not specified, it
+// defaults to "numerical".  "number2" is only used if "op" is "between".
+// "between" is "number <= * <= number2".
+// "number" is used for all "op" values except "any". "number" and "number2"
+// are in the format of x, x.x, x.x.x, etc.
+// Only "driver_version" supports lexical style if the format is major.minor;
+// in that case, major is still numerical, but minor is lexical. 
+//
+// STRING includes "op" and "value".  "op" can be any of the following values:
+// "contains", "beginwith", "endwith", "=".  "value" is a string.
+//
+// FLOAT includes "op" "value", and "value2".  "op" can be any of the
+// following values: "=", "<", "<=", ">", ">=", "any", "between".  "value2" is
+// only used if "op" is "between".  "value" is used for all "op" values except
+// "any". "value" and "value2" are valid float numbers.
+// INT is very much like FLOAT, except that the values need to be integers.
+
+#include "gpu/config/gpu_control_list_jsons.h"
+
+#define LONG_STRING_CONST(...) #__VA_ARGS__
+
+namespace gpu {
+
+const char kGpuSwitchingListJson[] = LONG_STRING_CONST(
+
+{
+  "name": "gpu switching list",
+  // Please update the version number whenever you change this file.
+  "version": "2.0",
+  "entries": [
+    {
+      "id": 1,
+      "description": "Force to use discrete GPU on older MacBookPro models.",
+      "cr_bugs": [113703],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": ">=",
+          "number": "10.7"
+        }
+      },
+      "machine_model": {
+        "name": {
+          "op": "=",
+          "value": "MacBookPro"
+        },
+        "version": {
+          "op": "<",
+          "number": "8"
+        }
+      },
+      "gpu_count": {
+        "op": "=",
+        "value": "2"
+      },
+      "features": [
+        "force_discrete"
+      ]
+    }
+  ]
+}
+
+);  // LONG_STRING_CONST macro
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_switching_list_unittest.cc b/gpu/config/gpu_switching_list_unittest.cc
new file mode 100644
index 0000000..78dc74d
--- /dev/null
+++ b/gpu/config/gpu_switching_list_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2013 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/scoped_ptr.h"
+#include "gpu/config/gpu_control_list_jsons.h"
+#include "gpu/config/gpu_info.h"
+#include "gpu/config/gpu_switching_list.h"
+#include "gpu/config/gpu_switching_option.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define LONG_STRING_CONST(...) #__VA_ARGS__
+
+const char kOsVersion[] = "10.6.4";
+
+namespace gpu {
+
+class GpuSwitchingListTest : public testing::Test {
+ public:
+  GpuSwitchingListTest() { }
+
+  virtual ~GpuSwitchingListTest() { }
+
+  const GPUInfo& gpu_info() const {
+    return gpu_info_;
+  }
+
+ protected:
+  virtual void SetUp() {
+    gpu_info_.gpu.vendor_id = 0x10de;
+    gpu_info_.gpu.device_id = 0x0640;
+    gpu_info_.driver_vendor = "NVIDIA";
+    gpu_info_.driver_version = "1.6.18";
+    gpu_info_.driver_date = "7-14-2009";
+    gpu_info_.machine_model = "MacBookPro 7.1";
+    gpu_info_.gl_vendor = "NVIDIA Corporation";
+    gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
+    gpu_info_.performance_stats.graphics = 5.0;
+    gpu_info_.performance_stats.gaming = 5.0;
+    gpu_info_.performance_stats.overall = 5.0;
+  }
+
+  virtual void TearDown() {
+  }
+
+ private:
+  GPUInfo gpu_info_;
+};
+
+TEST_F(GpuSwitchingListTest, CurrentSwitchingListValidation) {
+  scoped_ptr<GpuSwitchingList> switching_list(GpuSwitchingList::Create());
+  EXPECT_TRUE(switching_list->LoadList(
+      kGpuSwitchingListJson, GpuControlList::kAllOs));
+  EXPECT_FALSE(switching_list->contains_unknown_fields());
+}
+
+TEST_F(GpuSwitchingListTest, GpuSwitching) {
+  const std::string json = LONG_STRING_CONST(
+      {
+        "name": "gpu switching list",
+        "version": "0.1",
+        "entries": [
+          {
+            "id": 1,
+            "os": {
+              "type": "macosx"
+            },
+            "features": [
+              "force_discrete"
+            ]
+          },
+          {
+            "id": 2,
+            "os": {
+              "type": "win"
+            },
+            "features": [
+              "force_integrated"
+            ]
+          }
+        ]
+      }
+  );
+  scoped_ptr<GpuSwitchingList> switching_list(GpuSwitchingList::Create());
+  EXPECT_TRUE(switching_list->LoadList(json, GpuControlList::kAllOs));
+  std::set<int> switching = switching_list->MakeDecision(
+      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
+  EXPECT_EQ(1u, switching.size());
+  EXPECT_EQ(1u, switching.count(GPU_SWITCHING_OPTION_FORCE_DISCRETE));
+  std::vector<uint32> entries;
+  switching_list->GetDecisionEntries(&entries, false);
+  ASSERT_EQ(1u, entries.size());
+  EXPECT_EQ(1u, entries[0]);
+
+  switching_list.reset(GpuSwitchingList::Create());
+  EXPECT_TRUE(switching_list->LoadList(json, GpuControlList::kAllOs));
+  switching = switching_list->MakeDecision(
+      GpuControlList::kOsWin, kOsVersion, gpu_info());
+  EXPECT_EQ(1u, switching.size());
+  EXPECT_EQ(1u, switching.count(GPU_SWITCHING_OPTION_FORCE_INTEGRATED));
+  switching_list->GetDecisionEntries(&entries, false);
+  ASSERT_EQ(1u, entries.size());
+  EXPECT_EQ(2u, entries[0]);
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_switching_option.h b/gpu/config/gpu_switching_option.h
new file mode 100644
index 0000000..036c15f
--- /dev/null
+++ b/gpu/config/gpu_switching_option.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_SWITCHING_OPTION_H_
+#define GPU_CONFIG_GPU_SWITCHING_OPTION_H_
+
+//#include "build/build_config.h"
+
+namespace gpu {
+
+enum GpuSwitchingOption {
+  GPU_SWITCHING_OPTION_AUTOMATIC,
+  GPU_SWITCHING_OPTION_FORCE_INTEGRATED,
+  GPU_SWITCHING_OPTION_FORCE_DISCRETE,
+  GPU_SWITCHING_OPTION_UNKNOWN
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_SWITCHING_OPTION_H_
+
diff --git a/gpu/config/gpu_test_config.cc b/gpu/config/gpu_test_config.cc
new file mode 100644
index 0000000..ffb6e5f
--- /dev/null
+++ b/gpu/config/gpu_test_config.cc
@@ -0,0 +1,272 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_test_config.h"
+
+#include "base/logging.h"
+#include "base/sys_info.h"
+#include "gpu/config/gpu_info.h"
+#include "gpu/config/gpu_info_collector.h"
+#include "gpu/config/gpu_test_expectations_parser.h"
+
+namespace gpu {
+
+namespace {
+
+GPUTestConfig::OS GetCurrentOS() {
+#if defined(OS_CHROMEOS)
+  return GPUTestConfig::kOsChromeOS;
+#elif defined(OS_LINUX) || defined(OS_OPENBSD)
+  return GPUTestConfig::kOsLinux;
+#elif defined(OS_WIN)
+  int32 major_version = 0;
+  int32 minor_version = 0;
+  int32 bugfix_version = 0;
+  base::SysInfo::OperatingSystemVersionNumbers(
+      &major_version, &minor_version, &bugfix_version);
+  if (major_version == 5)
+    return GPUTestConfig::kOsWinXP;
+  if (major_version == 6 && minor_version == 0)
+    return GPUTestConfig::kOsWinVista;
+  if (major_version == 6 && minor_version == 1)
+    return GPUTestConfig::kOsWin7;
+  if (major_version == 6 && minor_version == 2)
+    return GPUTestConfig::kOsWin8;
+#elif defined(OS_MACOSX)
+  int32 major_version = 0;
+  int32 minor_version = 0;
+  int32 bugfix_version = 0;
+  base::SysInfo::OperatingSystemVersionNumbers(
+      &major_version, &minor_version, &bugfix_version);
+  if (major_version == 10) {
+    switch (minor_version) {
+      case 5:
+        return GPUTestConfig::kOsMacLeopard;
+      case 6:
+        return GPUTestConfig::kOsMacSnowLeopard;
+      case 7:
+        return GPUTestConfig::kOsMacLion;
+      case 8:
+        return GPUTestConfig::kOsMacMountainLion;
+    }
+  }
+#elif defined(OS_ANDROID)
+  return GPUTestConfig::kOsAndroid;
+#endif
+  return GPUTestConfig::kOsUnknown;
+}
+
+}  // namespace anonymous
+
+GPUTestConfig::GPUTestConfig()
+    : validate_gpu_info_(true),
+      os_(kOsUnknown),
+      gpu_device_id_(0),
+      build_type_(kBuildTypeUnknown) {
+}
+
+GPUTestConfig::~GPUTestConfig() {
+}
+
+void GPUTestConfig::set_os(int32 os) {
+  DCHECK_EQ(0, os & ~(kOsAndroid | kOsWin | kOsMac | kOsLinux | kOsChromeOS));
+  os_ = os;
+}
+
+void GPUTestConfig::AddGPUVendor(uint32 gpu_vendor) {
+  DCHECK_NE(0u, gpu_vendor);
+  for (size_t i = 0; i < gpu_vendor_.size(); ++i)
+    DCHECK_NE(gpu_vendor_[i], gpu_vendor);
+  gpu_vendor_.push_back(gpu_vendor);
+}
+
+void GPUTestConfig::set_gpu_device_id(uint32 id) {
+  gpu_device_id_ = id;
+}
+
+void GPUTestConfig::set_build_type(int32 build_type) {
+  DCHECK_EQ(0, build_type & ~(kBuildTypeRelease | kBuildTypeDebug));
+  build_type_ = build_type;
+}
+
+bool GPUTestConfig::IsValid() const {
+  if (!validate_gpu_info_)
+    return true;
+  if (gpu_device_id_ != 0 && (gpu_vendor_.size() != 1 || gpu_vendor_[0] == 0))
+    return false;
+  return true;
+}
+
+bool GPUTestConfig::OverlapsWith(const GPUTestConfig& config) const {
+  DCHECK(IsValid());
+  DCHECK(config.IsValid());
+  if (config.os_ != kOsUnknown && os_ != kOsUnknown &&
+      (os_ & config.os_) == 0)
+    return false;
+  if (config.gpu_vendor_.size() > 0 && gpu_vendor_.size() > 0) {
+    bool shared = false;
+    for (size_t i = 0; i < config.gpu_vendor_.size() && !shared; ++i) {
+      for (size_t j = 0; j < gpu_vendor_.size(); ++j) {
+        if (config.gpu_vendor_[i] == gpu_vendor_[j]) {
+          shared = true;
+          break;
+        }
+      }
+    }
+    if (!shared)
+      return false;
+  }
+  if (config.gpu_device_id_ != 0 && gpu_device_id_ != 0 &&
+      gpu_device_id_ != config.gpu_device_id_)
+    return false;
+  if (config.build_type_ != kBuildTypeUnknown &&
+      build_type_ != kBuildTypeUnknown &&
+      (build_type_ & config.build_type_) == 0)
+    return false;
+  return true;
+}
+
+void GPUTestConfig::DisableGPUInfoValidation() {
+  validate_gpu_info_ = false;
+}
+
+void GPUTestConfig::ClearGPUVendor() {
+  gpu_vendor_.clear();
+}
+
+GPUTestBotConfig::~GPUTestBotConfig() {
+}
+
+void GPUTestBotConfig::AddGPUVendor(uint32 gpu_vendor) {
+  DCHECK_EQ(0u, GPUTestConfig::gpu_vendor().size());
+  GPUTestConfig::AddGPUVendor(gpu_vendor);
+}
+
+bool GPUTestBotConfig::SetGPUInfo(const GPUInfo& gpu_info) {
+  DCHECK(validate_gpu_info_);
+  if (gpu_info.gpu.device_id == 0 || gpu_info.gpu.vendor_id == 0)
+    return false;
+  ClearGPUVendor();
+  AddGPUVendor(gpu_info.gpu.vendor_id);
+  set_gpu_device_id(gpu_info.gpu.device_id);
+  return true;
+}
+
+bool GPUTestBotConfig::IsValid() const {
+  switch (os()) {
+    case kOsWinXP:
+    case kOsWinVista:
+    case kOsWin7:
+    case kOsWin8:
+    case kOsMacLeopard:
+    case kOsMacSnowLeopard:
+    case kOsMacLion:
+    case kOsMacMountainLion:
+    case kOsLinux:
+    case kOsChromeOS:
+    case kOsAndroid:
+      break;
+    default:
+      return false;
+  }
+  if (validate_gpu_info_) {
+    if (gpu_vendor().size() != 1 || gpu_vendor()[0] == 0)
+      return false;
+    if (gpu_device_id() == 0)
+      return false;
+  }
+  switch (build_type()) {
+    case kBuildTypeRelease:
+    case kBuildTypeDebug:
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+bool GPUTestBotConfig::Matches(const GPUTestConfig& config) const {
+  DCHECK(IsValid());
+  DCHECK(config.IsValid());
+  if (config.os() != kOsUnknown && (os() & config.os()) == 0)
+    return false;
+  if (config.gpu_vendor().size() > 0) {
+    bool contained = false;
+    for (size_t i = 0; i < config.gpu_vendor().size(); ++i) {
+      if (config.gpu_vendor()[i] == gpu_vendor()[0]) {
+        contained = true;
+        break;
+      }
+    }
+    if (!contained)
+      return false;
+  }
+  if (config.gpu_device_id() != 0 &&
+      gpu_device_id() != config.gpu_device_id())
+    return false;
+  if (config.build_type() != kBuildTypeUnknown &&
+      (build_type() & config.build_type()) == 0)
+    return false;
+  return true;
+}
+
+bool GPUTestBotConfig::Matches(const std::string& config_data) const {
+  GPUTestExpectationsParser parser;
+  GPUTestConfig config;
+
+  if (!parser.ParseConfig(config_data, &config))
+    return false;
+  return Matches(config);
+}
+
+bool GPUTestBotConfig::LoadCurrentConfig(const GPUInfo* gpu_info) {
+  bool rt;
+  if (gpu_info == NULL) {
+    GPUInfo my_gpu_info;
+    GpuIDResult result;
+    result = CollectGpuID(&my_gpu_info.gpu.vendor_id,
+                          &my_gpu_info.gpu.device_id);
+    if (result == kGpuIDNotSupported) {
+      DisableGPUInfoValidation();
+      rt = true;
+    } else {
+      rt = SetGPUInfo(my_gpu_info);
+    }
+  } else {
+    rt = SetGPUInfo(*gpu_info);
+  }
+  set_os(GetCurrentOS());
+  if (os() == kOsUnknown)
+    rt = false;
+#if defined(NDEBUG)
+  set_build_type(kBuildTypeRelease);
+#else
+  set_build_type(kBuildTypeDebug);
+#endif
+  return rt;
+}
+
+// static
+bool GPUTestBotConfig::CurrentConfigMatches(const std::string& config_data) {
+  GPUTestBotConfig my_config;
+  if (!my_config.LoadCurrentConfig(NULL))
+    return false;
+  return my_config.Matches(config_data);
+}
+
+// static
+bool GPUTestBotConfig::CurrentConfigMatches(
+    const std::vector<std::string>& configs) {
+  GPUTestBotConfig my_config;
+  if (!my_config.LoadCurrentConfig(NULL))
+    return false;
+  for (size_t i = 0 ; i < configs.size(); ++i) {
+    if (my_config.Matches(configs[i]))
+      return true;
+  }
+  return false;
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_test_config.h b/gpu/config/gpu_test_config.h
new file mode 100644
index 0000000..3c7b038
--- /dev/null
+++ b/gpu/config/gpu_test_config.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_TEST_CONFIG_H_
+#define GPU_CONFIG_GPU_TEST_CONFIG_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+struct GPUInfo;
+
+class GPU_EXPORT GPUTestConfig {
+ public:
+  enum OS {
+    kOsUnknown = 0,
+    kOsWinXP = 1 << 0,
+    kOsWinVista = 1 << 1,
+    kOsWin7 = 1 << 2,
+    kOsWin8 = 1 << 3,
+    kOsWin = kOsWinXP | kOsWinVista | kOsWin7 | kOsWin8,
+    kOsMacLeopard = 1 << 4,
+    kOsMacSnowLeopard = 1 << 5,
+    kOsMacLion = 1 << 6,
+    kOsMacMountainLion = 1 << 7,
+    kOsMac = kOsMacLeopard | kOsMacSnowLeopard | kOsMacLion |
+             kOsMacMountainLion,
+    kOsLinux = 1 << 8,
+    kOsChromeOS = 1 << 9,
+    kOsAndroid = 1 << 10,
+  };
+
+  enum BuildType {
+    kBuildTypeUnknown = 0,
+    kBuildTypeRelease = 1 << 0,
+    kBuildTypeDebug = 1 << 1,
+  };
+
+  GPUTestConfig();
+  virtual ~GPUTestConfig();
+
+  void set_os(int32 os);
+  void set_gpu_device_id(uint32 id);
+  void set_build_type(int32 build_type);
+
+  virtual void AddGPUVendor(uint32 gpu_vendor);
+
+  int32 os() const { return os_; }
+  const std::vector<uint32>& gpu_vendor() const { return gpu_vendor_; }
+  uint32 gpu_device_id() const { return gpu_device_id_; }
+  int32 build_type() const { return build_type_; }
+
+  // Check if the config is valid. For example, if gpu_device_id_ is set, but
+  // gpu_vendor_ is unknown, then it's invalid.
+  virtual bool IsValid() const;
+
+  // Check if two configs overlap, i.e., if there exists a config that matches
+  // both configs.
+  bool OverlapsWith(const GPUTestConfig& config) const;
+
+  // Disable validation of GPU vendor and device ids.
+  void DisableGPUInfoValidation();
+
+ protected:
+  void ClearGPUVendor();
+
+  // Indicates that the OS has the notion of a numeric GPU vendor and device id
+  // and this data should be validated.
+  bool validate_gpu_info_;
+
+ private:
+  // operating system.
+  int32 os_;
+
+  // GPU vendor.
+  std::vector<uint32> gpu_vendor_;
+
+  // GPU device id (unique to each vendor).
+  uint32 gpu_device_id_;
+
+  // Release or Debug.
+  int32 build_type_;
+};
+
+class GPU_EXPORT GPUTestBotConfig : public GPUTestConfig {
+ public:
+  GPUTestBotConfig() { }
+  virtual ~GPUTestBotConfig();
+
+  // This should only be called when no gpu_vendor is added.
+  virtual void AddGPUVendor(uint32 gpu_vendor) OVERRIDE;
+
+  // Return false if gpu_info does not have valid vendor_id and device_id.
+  bool SetGPUInfo(const GPUInfo& gpu_info);
+
+  // Check if the bot config is valid, i.e., if it is one valid test-bot
+  // environment. For example, if a field is unknown, or if OS is not one
+  // fully defined OS, then it's valid.
+  virtual bool IsValid() const OVERRIDE;
+
+  // Check if a bot config matches a test config, i.e., the test config is a
+  // superset of the bot config.
+  bool Matches(const GPUTestConfig& config) const;
+  bool Matches(const std::string& config_data) const;
+
+  // Setup the config with the current gpu testing environment.
+  // If gpu_info is NULL, collect GPUInfo first.
+  bool LoadCurrentConfig(const GPUInfo* gpu_info);
+
+  // Check if this bot's config matches |config_data| or any of the |configs|.
+  static bool CurrentConfigMatches(const std::string& config_data);
+  static bool CurrentConfigMatches(const std::vector<std::string>& configs);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_TEST_CONFIG_H_
+
diff --git a/gpu/config/gpu_test_config_unittest.cc b/gpu/config/gpu_test_config_unittest.cc
new file mode 100644
index 0000000..b9411dc
--- /dev/null
+++ b/gpu/config/gpu_test_config_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_info.h"
+#include "gpu/config/gpu_test_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class GPUTestConfigTest : public testing::Test {
+ public:
+  GPUTestConfigTest() { }
+
+  virtual ~GPUTestConfigTest() { }
+
+ protected:
+  virtual void SetUp() { }
+
+  virtual void TearDown() { }
+};
+
+TEST_F(GPUTestConfigTest, EmptyValues) {
+  GPUTestConfig config;
+  EXPECT_EQ(GPUTestConfig::kOsUnknown, config.os());
+  EXPECT_EQ(0u, config.gpu_vendor().size());
+  EXPECT_EQ(0u, config.gpu_device_id());
+  EXPECT_EQ(GPUTestConfig::kBuildTypeUnknown, config.build_type());
+}
+
+TEST_F(GPUTestConfigTest, SetGPUInfo) {
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x10de;
+  gpu_info.gpu.device_id = 0x0640;
+  GPUTestBotConfig config;
+  EXPECT_TRUE(config.SetGPUInfo(gpu_info));
+  EXPECT_EQ(1u, config.gpu_vendor().size());
+  EXPECT_EQ(gpu_info.gpu.vendor_id, config.gpu_vendor()[0]);
+  EXPECT_EQ(gpu_info.gpu.device_id, config.gpu_device_id());
+
+  gpu_info.gpu.vendor_id = 0x8086;
+  gpu_info.gpu.device_id = 0x0046;
+  EXPECT_TRUE(config.SetGPUInfo(gpu_info));
+  EXPECT_EQ(1u, config.gpu_vendor().size());
+  EXPECT_EQ(gpu_info.gpu.vendor_id, config.gpu_vendor()[0]);
+  EXPECT_EQ(gpu_info.gpu.device_id, config.gpu_device_id());
+}
+
+TEST_F(GPUTestConfigTest, IsValid) {
+  {
+    GPUTestConfig config;
+    config.set_gpu_device_id(0x0640);
+    EXPECT_FALSE(config.IsValid());
+    config.AddGPUVendor(0x10de);
+    EXPECT_TRUE(config.IsValid());
+  }
+
+  {
+    GPUTestBotConfig config;
+    config.set_build_type(GPUTestConfig::kBuildTypeRelease);
+    config.set_os(GPUTestConfig::kOsWin7);
+    config.set_gpu_device_id(0x0640);
+    EXPECT_FALSE(config.IsValid());
+    config.AddGPUVendor(0x10de);
+    EXPECT_TRUE(config.IsValid());
+
+    config.set_gpu_device_id(0);
+    EXPECT_FALSE(config.IsValid());
+    config.set_gpu_device_id(0x0640);
+    EXPECT_TRUE(config.IsValid());
+
+    config.set_os(GPUTestConfig::kOsWin);
+    EXPECT_FALSE(config.IsValid());
+    config.set_os(GPUTestConfig::kOsWin7 | GPUTestConfig::kOsWinXP);
+    EXPECT_FALSE(config.IsValid());
+    config.set_os(GPUTestConfig::kOsWin7);
+    EXPECT_TRUE(config.IsValid());
+
+    config.set_build_type(GPUTestConfig::kBuildTypeUnknown);
+    EXPECT_FALSE(config.IsValid());
+    config.set_build_type(GPUTestConfig::kBuildTypeRelease);
+    EXPECT_TRUE(config.IsValid());
+  }
+}
+
+TEST_F(GPUTestConfigTest, Matches) {
+  GPUTestBotConfig config;
+  config.set_os(GPUTestConfig::kOsWin7);
+  config.set_build_type(GPUTestConfig::kBuildTypeRelease);
+  config.AddGPUVendor(0x10de);
+  config.set_gpu_device_id(0x0640);
+  EXPECT_TRUE(config.IsValid());
+
+  {  // os matching
+    GPUTestConfig config2;
+    EXPECT_TRUE(config.Matches(config2));
+    config2.set_os(GPUTestConfig::kOsWin);
+    EXPECT_TRUE(config.Matches(config2));
+    config2.set_os(GPUTestConfig::kOsWin7);
+    EXPECT_TRUE(config.Matches(config2));
+    config2.set_os(GPUTestConfig::kOsMac);
+    EXPECT_FALSE(config.Matches(config2));
+    config2.set_os(GPUTestConfig::kOsWin7 | GPUTestConfig::kOsLinux);
+    EXPECT_TRUE(config.Matches(config2));
+  }
+
+  {  // gpu vendor matching
+    {
+      GPUTestConfig config2;
+      config2.AddGPUVendor(0x10de);
+      EXPECT_TRUE(config.Matches(config2));
+      config2.AddGPUVendor(0x1004);
+      EXPECT_TRUE(config.Matches(config2));
+    }
+    {
+      GPUTestConfig config2;
+      config2.AddGPUVendor(0x8086);
+      EXPECT_FALSE(config.Matches(config2));
+    }
+  }
+
+  {  // build type matching
+    GPUTestConfig config2;
+    config2.set_build_type(GPUTestConfig::kBuildTypeRelease);
+    EXPECT_TRUE(config.Matches(config2));
+    config2.set_build_type(GPUTestConfig::kBuildTypeRelease |
+                           GPUTestConfig::kBuildTypeDebug);
+    EXPECT_TRUE(config.Matches(config2));
+    config2.set_build_type(GPUTestConfig::kBuildTypeDebug);
+    EXPECT_FALSE(config.Matches(config2));
+  }
+
+  {  // exact matching
+    GPUTestConfig config2;
+    config2.set_os(GPUTestConfig::kOsWin7);
+    config2.set_build_type(GPUTestConfig::kBuildTypeRelease);
+    config2.AddGPUVendor(0x10de);
+    config2.set_gpu_device_id(0x0640);
+    EXPECT_TRUE(config.Matches(config2));
+    config2.set_gpu_device_id(0x0641);
+    EXPECT_FALSE(config.Matches(config2));
+  }
+}
+
+TEST_F(GPUTestConfigTest, StringMatches) {
+  GPUTestBotConfig config;
+  config.set_os(GPUTestConfig::kOsWin7);
+  config.set_build_type(GPUTestConfig::kBuildTypeRelease);
+  config.AddGPUVendor(0x10de);
+  config.set_gpu_device_id(0x0640);
+  EXPECT_TRUE(config.IsValid());
+
+  EXPECT_TRUE(config.Matches(std::string()));
+
+  // os matching
+  EXPECT_TRUE(config.Matches("WIN"));
+  EXPECT_TRUE(config.Matches("WIN7"));
+  EXPECT_FALSE(config.Matches("MAC"));
+  EXPECT_TRUE(config.Matches("WIN7 LINUX"));
+
+  // gpu vendor matching
+  EXPECT_TRUE(config.Matches("NVIDIA"));
+  EXPECT_TRUE(config.Matches("NVIDIA AMD"));
+  EXPECT_FALSE(config.Matches("INTEL"));
+
+  // build type matching
+  EXPECT_TRUE(config.Matches("RELEASE"));
+  EXPECT_TRUE(config.Matches("RELEASE DEBUG"));
+  EXPECT_FALSE(config.Matches("DEBUG"));
+
+  // exact matching
+  EXPECT_TRUE(config.Matches("WIN7 RELEASE NVIDIA 0X0640"));
+  EXPECT_FALSE(config.Matches("WIN7 RELEASE NVIDIA 0X0641"));
+}
+
+TEST_F(GPUTestConfigTest, OverlapsWith) {
+  {  // os
+    // win vs win7
+    GPUTestConfig config;
+    config.set_os(GPUTestConfig::kOsWin);
+    GPUTestConfig config2;
+    config2.set_os(GPUTestConfig::kOsWin7);
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+    // win vs win7+linux
+    config2.set_os(GPUTestConfig::kOsWin7 | GPUTestConfig::kOsLinux);
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+    // win vs mac
+    config2.set_os(GPUTestConfig::kOsMac);
+    EXPECT_FALSE(config.OverlapsWith(config2));
+    EXPECT_FALSE(config2.OverlapsWith(config));
+    // win vs unknown
+    config2.set_os(GPUTestConfig::kOsUnknown);
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+  }
+
+  {  // gpu vendor
+    GPUTestConfig config;
+    config.AddGPUVendor(0x10de);
+    // nvidia vs unknown
+    GPUTestConfig config2;
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+    // nvidia vs intel
+    config2.AddGPUVendor(0x1086);
+    EXPECT_FALSE(config.OverlapsWith(config2));
+    EXPECT_FALSE(config2.OverlapsWith(config));
+    // nvidia vs nvidia+intel
+    config2.AddGPUVendor(0x10de);
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+  }
+
+  {  // build type
+    // release vs debug
+    GPUTestConfig config;
+    config.set_build_type(GPUTestConfig::kBuildTypeRelease);
+    GPUTestConfig config2;
+    config2.set_build_type(GPUTestConfig::kBuildTypeDebug);
+    EXPECT_FALSE(config.OverlapsWith(config2));
+    EXPECT_FALSE(config2.OverlapsWith(config));
+    // release vs release+debug
+    config2.set_build_type(GPUTestConfig::kBuildTypeRelease |
+                           GPUTestConfig::kBuildTypeDebug);
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+    // release vs unknown
+    config2.set_build_type(GPUTestConfig::kBuildTypeUnknown);
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+  }
+
+  {  // win7 vs nvidia
+    GPUTestConfig config;
+    config.set_os(GPUTestConfig::kOsWin7);
+    GPUTestConfig config2;
+    config2.AddGPUVendor(0x10de);
+    EXPECT_TRUE(config.OverlapsWith(config2));
+    EXPECT_TRUE(config2.OverlapsWith(config));
+  }
+}
+
+TEST_F(GPUTestConfigTest, LoadCurrentConfig) {
+  GPUTestBotConfig config;
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x10de;
+  gpu_info.gpu.device_id = 0x0640;
+  EXPECT_TRUE(config.LoadCurrentConfig(&gpu_info));
+  EXPECT_TRUE(config.IsValid());
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_test_expectations_parser.cc b/gpu/config/gpu_test_expectations_parser.cc
new file mode 100644
index 0000000..8cc2b3c
--- /dev/null
+++ b/gpu/config/gpu_test_expectations_parser.cc
@@ -0,0 +1,503 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_test_expectations_parser.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/strings/string_split.h"
+
+namespace gpu {
+
+namespace {
+
+enum LineParserStage {
+  kLineParserBegin = 0,
+  kLineParserBugID,
+  kLineParserConfigs,
+  kLineParserColon,
+  kLineParserTestName,
+  kLineParserEqual,
+  kLineParserExpectations,
+};
+
+enum Token {
+  // os
+  kConfigWinXP = 0,
+  kConfigWinVista,
+  kConfigWin7,
+  kConfigWin8,
+  kConfigWin,
+  kConfigMacLeopard,
+  kConfigMacSnowLeopard,
+  kConfigMacLion,
+  kConfigMacMountainLion,
+  kConfigMac,
+  kConfigLinux,
+  kConfigChromeOS,
+  kConfigAndroid,
+  // gpu vendor
+  kConfigNVidia,
+  kConfigAMD,
+  kConfigIntel,
+  kConfigVMWare,
+  // build type
+  kConfigRelease,
+  kConfigDebug,
+  // expectation
+  kExpectationPass,
+  kExpectationFail,
+  kExpectationFlaky,
+  kExpectationTimeout,
+  kExpectationSkip,
+  // separator
+  kSeparatorColon,
+  kSeparatorEqual,
+
+  kNumberOfExactMatchTokens,
+
+  // others
+  kConfigGPUDeviceID,
+  kTokenComment,
+  kTokenWord,
+};
+
+struct TokenInfo {
+  const char* name;
+  int32 flag;
+};
+
+const TokenInfo kTokenData[] = {
+  { "xp", GPUTestConfig::kOsWinXP },
+  { "vista", GPUTestConfig::kOsWinVista },
+  { "win7", GPUTestConfig::kOsWin7 },
+  { "win8", GPUTestConfig::kOsWin8 },
+  { "win", GPUTestConfig::kOsWin },
+  { "leopard", GPUTestConfig::kOsMacLeopard },
+  { "snowleopard", GPUTestConfig::kOsMacSnowLeopard },
+  { "lion", GPUTestConfig::kOsMacLion },
+  { "mountainlion", GPUTestConfig::kOsMacMountainLion },
+  { "mac", GPUTestConfig::kOsMac },
+  { "linux", GPUTestConfig::kOsLinux },
+  { "chromeos", GPUTestConfig::kOsChromeOS },
+  { "android", GPUTestConfig::kOsAndroid },
+  { "nvidia", 0x10DE },
+  { "amd", 0x1002 },
+  { "intel", 0x8086 },
+  { "vmware", 0x15ad },
+  { "release", GPUTestConfig::kBuildTypeRelease },
+  { "debug", GPUTestConfig::kBuildTypeDebug },
+  { "pass", GPUTestExpectationsParser::kGpuTestPass },
+  { "fail", GPUTestExpectationsParser::kGpuTestFail },
+  { "flaky", GPUTestExpectationsParser::kGpuTestFlaky },
+  { "timeout", GPUTestExpectationsParser::kGpuTestTimeout },
+  { "skip", GPUTestExpectationsParser::kGpuTestSkip },
+  { ":", 0 },
+  { "=", 0 },
+};
+
+enum ErrorType {
+  kErrorFileIO = 0,
+  kErrorIllegalEntry,
+  kErrorInvalidEntry,
+  kErrorEntryWithOsConflicts,
+  kErrorEntryWithGpuVendorConflicts,
+  kErrorEntryWithBuildTypeConflicts,
+  kErrorEntryWithGpuDeviceIdConflicts,
+  kErrorEntryWithExpectationConflicts,
+  kErrorEntriesOverlap,
+
+  kNumberOfErrors,
+};
+
+const char* kErrorMessage[] = {
+  "file IO failed",
+  "entry with wrong format",
+  "entry invalid, likely wrong modifiers combination",
+  "entry with OS modifier conflicts",
+  "entry with GPU vendor modifier conflicts",
+  "entry with GPU build type conflicts",
+  "entry with GPU device id conflicts or malformat",
+  "entry with expectation modifier conflicts",
+  "two entries's configs overlap",
+};
+
+Token ParseToken(const std::string& word) {
+  if (StartsWithASCII(word, "//", false))
+    return kTokenComment;
+  if (StartsWithASCII(word, "0x", false))
+    return kConfigGPUDeviceID;
+
+  for (int32 i = 0; i < kNumberOfExactMatchTokens; ++i) {
+    if (LowerCaseEqualsASCII(word, kTokenData[i].name))
+      return static_cast<Token>(i);
+  }
+  return kTokenWord;
+}
+
+// reference name can have the last character as *.
+bool NamesMatching(const std::string& ref, const std::string& test_name) {
+  size_t len = ref.length();
+  if (len == 0)
+    return false;
+  if (ref[len - 1] == '*') {
+    if (test_name.length() > len -1 &&
+        ref.compare(0, len - 1, test_name, 0, len - 1) == 0)
+      return true;
+    return false;
+  }
+  return (ref == test_name);
+}
+
+}  // namespace anonymous
+
+GPUTestExpectationsParser::GPUTestExpectationsParser() {
+  // Some sanity check.
+  DCHECK_EQ(static_cast<unsigned int>(kNumberOfExactMatchTokens),
+            sizeof(kTokenData) / sizeof(kTokenData[0]));
+  DCHECK_EQ(static_cast<unsigned int>(kNumberOfErrors),
+            sizeof(kErrorMessage) / sizeof(kErrorMessage[0]));
+}
+
+GPUTestExpectationsParser::~GPUTestExpectationsParser() {
+}
+
+bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) {
+  entries_.clear();
+  error_messages_.clear();
+
+  std::vector<std::string> lines;
+  base::SplitString(data, '\n', &lines);
+  bool rt = true;
+  for (size_t i = 0; i < lines.size(); ++i) {
+    if (!ParseLine(lines[i], i + 1))
+      rt = false;
+  }
+  if (DetectConflictsBetweenEntries()) {
+    entries_.clear();
+    rt = false;
+  }
+
+  return rt;
+}
+
+bool GPUTestExpectationsParser::LoadTestExpectations(
+    const base::FilePath& path) {
+  entries_.clear();
+  error_messages_.clear();
+
+  std::string data;
+  if (!file_util::ReadFileToString(path, &data)) {
+    error_messages_.push_back(kErrorMessage[kErrorFileIO]);
+    return false;
+  }
+  return LoadTestExpectations(data);
+}
+
+int32 GPUTestExpectationsParser::GetTestExpectation(
+    const std::string& test_name,
+    const GPUTestBotConfig& bot_config) const {
+  for (size_t i = 0; i < entries_.size(); ++i) {
+    if (NamesMatching(entries_[i].test_name, test_name) &&
+        bot_config.Matches(entries_[i].test_config))
+      return entries_[i].test_expectation;
+  }
+  return kGpuTestPass;
+}
+
+const std::vector<std::string>&
+GPUTestExpectationsParser::GetErrorMessages() const {
+  return error_messages_;
+}
+
+bool GPUTestExpectationsParser::ParseConfig(
+    const std::string& config_data, GPUTestConfig* config) {
+  DCHECK(config);
+  std::vector<std::string> tokens;
+  base::SplitStringAlongWhitespace(config_data, &tokens);
+
+  for (size_t i = 0; i < tokens.size(); ++i) {
+    Token token = ParseToken(tokens[i]);
+    switch (token) {
+      case kConfigWinXP:
+      case kConfigWinVista:
+      case kConfigWin7:
+      case kConfigWin8:
+      case kConfigWin:
+      case kConfigMacLeopard:
+      case kConfigMacSnowLeopard:
+      case kConfigMacLion:
+      case kConfigMacMountainLion:
+      case kConfigMac:
+      case kConfigLinux:
+      case kConfigChromeOS:
+      case kConfigAndroid:
+      case kConfigNVidia:
+      case kConfigAMD:
+      case kConfigIntel:
+      case kConfigVMWare:
+      case kConfigRelease:
+      case kConfigDebug:
+      case kConfigGPUDeviceID:
+        if (token == kConfigGPUDeviceID) {
+          if (!UpdateTestConfig(config, tokens[i], 0))
+            return false;
+        } else {
+          if (!UpdateTestConfig(config, token, 0))
+            return false;
+        }
+        break;
+      default:
+        return false;
+    }
+  }
+  return true;
+}
+
+bool GPUTestExpectationsParser::ParseLine(
+    const std::string& line_data, size_t line_number) {
+  std::vector<std::string> tokens;
+  base::SplitStringAlongWhitespace(line_data, &tokens);
+  int32 stage = kLineParserBegin;
+  GPUTestExpectationEntry entry;
+  entry.line_number = line_number;
+  GPUTestConfig& config = entry.test_config;
+  bool comments_encountered = false;
+  for (size_t i = 0; i < tokens.size() && !comments_encountered; ++i) {
+    Token token = ParseToken(tokens[i]);
+    switch (token) {
+      case kTokenComment:
+        comments_encountered = true;
+        break;
+      case kConfigWinXP:
+      case kConfigWinVista:
+      case kConfigWin7:
+      case kConfigWin8:
+      case kConfigWin:
+      case kConfigMacLeopard:
+      case kConfigMacSnowLeopard:
+      case kConfigMacLion:
+      case kConfigMacMountainLion:
+      case kConfigMac:
+      case kConfigLinux:
+      case kConfigChromeOS:
+      case kConfigAndroid:
+      case kConfigNVidia:
+      case kConfigAMD:
+      case kConfigIntel:
+      case kConfigVMWare:
+      case kConfigRelease:
+      case kConfigDebug:
+      case kConfigGPUDeviceID:
+        // MODIFIERS, could be in any order, need at least one.
+        if (stage != kLineParserConfigs && stage != kLineParserBugID) {
+          PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
+                           line_number);
+          return false;
+        }
+        if (token == kConfigGPUDeviceID) {
+          if (!UpdateTestConfig(&config, tokens[i], line_number))
+            return false;
+        } else {
+          if (!UpdateTestConfig(&config, token, line_number))
+            return false;
+        }
+        if (stage == kLineParserBugID)
+          stage++;
+        break;
+      case kSeparatorColon:
+        // :
+        if (stage != kLineParserConfigs) {
+          PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
+                           line_number);
+          return false;
+        }
+        stage++;
+        break;
+      case kSeparatorEqual:
+        // =
+        if (stage != kLineParserTestName) {
+          PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
+                           line_number);
+          return false;
+        }
+        stage++;
+        break;
+      case kTokenWord:
+        // BUG_ID or TEST_NAME
+        if (stage == kLineParserBegin) {
+          // Bug ID is not used for anything; ignore it.
+        } else if (stage == kLineParserColon) {
+          entry.test_name = tokens[i];
+        } else {
+          PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
+                           line_number);
+          return false;
+        }
+        stage++;
+        break;
+      case kExpectationPass:
+      case kExpectationFail:
+      case kExpectationFlaky:
+      case kExpectationTimeout:
+      case kExpectationSkip:
+        // TEST_EXPECTATIONS
+        if (stage != kLineParserEqual && stage != kLineParserExpectations) {
+          PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
+                           line_number);
+          return false;
+        }
+        if ((kTokenData[token].flag & entry.test_expectation) != 0) {
+          PushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],
+                           line_number);
+          return false;
+        }
+        entry.test_expectation =
+            (kTokenData[token].flag | entry.test_expectation);
+        if (stage == kLineParserEqual)
+          stage++;
+        break;
+      default:
+        DCHECK(false);
+        break;
+    }
+  }
+  if (stage == kLineParserBegin) {
+    // The whole line is empty or all comments
+    return true;
+  }
+  if (stage == kLineParserExpectations) {
+    if (!config.IsValid()) {
+        PushErrorMessage(kErrorMessage[kErrorInvalidEntry], line_number);
+        return false;
+    }
+    entries_.push_back(entry);
+    return true;
+  }
+  PushErrorMessage(kErrorMessage[kErrorIllegalEntry], line_number);
+  return false;
+}
+
+bool GPUTestExpectationsParser::UpdateTestConfig(
+    GPUTestConfig* config, int32 token, size_t line_number) {
+  DCHECK(config);
+  switch (token) {
+    case kConfigWinXP:
+    case kConfigWinVista:
+    case kConfigWin7:
+    case kConfigWin8:
+    case kConfigWin:
+    case kConfigMacLeopard:
+    case kConfigMacSnowLeopard:
+    case kConfigMacLion:
+    case kConfigMacMountainLion:
+    case kConfigMac:
+    case kConfigLinux:
+    case kConfigChromeOS:
+    case kConfigAndroid:
+      if ((config->os() & kTokenData[token].flag) != 0) {
+        PushErrorMessage(kErrorMessage[kErrorEntryWithOsConflicts],
+                         line_number);
+        return false;
+      }
+      config->set_os(config->os() | kTokenData[token].flag);
+      break;
+    case kConfigNVidia:
+    case kConfigAMD:
+    case kConfigIntel:
+    case kConfigVMWare:
+      {
+        uint32 gpu_vendor =
+            static_cast<uint32>(kTokenData[token].flag);
+        for (size_t i = 0; i < config->gpu_vendor().size(); ++i) {
+          if (config->gpu_vendor()[i] == gpu_vendor) {
+            PushErrorMessage(
+                kErrorMessage[kErrorEntryWithGpuVendorConflicts],
+                line_number);
+            return false;
+          }
+        }
+        config->AddGPUVendor(gpu_vendor);
+      }
+      break;
+    case kConfigRelease:
+    case kConfigDebug:
+      if ((config->build_type() & kTokenData[token].flag) != 0) {
+        PushErrorMessage(
+            kErrorMessage[kErrorEntryWithBuildTypeConflicts],
+            line_number);
+        return false;
+      }
+      config->set_build_type(
+          config->build_type() | kTokenData[token].flag);
+      break;
+    default:
+      DCHECK(false);
+      break;
+  }
+  return true;
+}
+
+bool GPUTestExpectationsParser::UpdateTestConfig(
+    GPUTestConfig* config,
+    const std::string& gpu_device_id,
+    size_t line_number) {
+  DCHECK(config);
+  uint32 device_id = 0;
+  if (config->gpu_device_id() != 0 ||
+      !base::HexStringToInt(gpu_device_id,
+                            reinterpret_cast<int*>(&device_id)) ||
+      device_id == 0) {
+    PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts],
+                     line_number);
+    return false;
+  }
+  config->set_gpu_device_id(device_id);
+  return true;
+}
+
+bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
+  bool rt = false;
+  for (size_t i = 0; i < entries_.size(); ++i) {
+    for (size_t j = i + 1; j < entries_.size(); ++j) {
+      if (entries_[i].test_name == entries_[j].test_name &&
+          entries_[i].test_config.OverlapsWith(entries_[j].test_config)) {
+        PushErrorMessage(kErrorMessage[kErrorEntriesOverlap],
+                         entries_[i].line_number,
+                         entries_[j].line_number);
+        rt = true;
+      }
+    }
+  }
+  return rt;
+}
+
+void GPUTestExpectationsParser::PushErrorMessage(
+    const std::string& message, size_t line_number) {
+  error_messages_.push_back(
+      base::StringPrintf("Line %d : %s",
+                         static_cast<int>(line_number), message.c_str()));
+}
+
+void GPUTestExpectationsParser::PushErrorMessage(
+    const std::string& message,
+    size_t entry1_line_number,
+    size_t entry2_line_number) {
+  error_messages_.push_back(
+      base::StringPrintf("Line %d and %d : %s",
+                         static_cast<int>(entry1_line_number),
+                         static_cast<int>(entry2_line_number),
+                         message.c_str()));
+}
+
+GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
+    : test_expectation(0),
+      line_number(0) {
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_test_expectations_parser.h b/gpu/config/gpu_test_expectations_parser.h
new file mode 100644
index 0000000..a69f7e9
--- /dev/null
+++ b/gpu/config/gpu_test_expectations_parser.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_TEST_EXPECTATIONS_PARSER_H_
+#define GPU_CONFIG_GPU_TEST_EXPECTATIONS_PARSER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "gpu/config/gpu_test_config.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+
+class GPU_EXPORT GPUTestExpectationsParser {
+ public:
+  enum GPUTestExpectation {
+    kGpuTestPass = 1 << 0,
+    kGpuTestFail = 1 << 1,
+    kGpuTestFlaky = 1 << 2,
+    kGpuTestTimeout = 1 << 3,
+    kGpuTestSkip = 1 << 4,
+  };
+
+  GPUTestExpectationsParser();
+  ~GPUTestExpectationsParser();
+
+  // Parse the text expectations, and if no error is encountered,
+  // save all the entries. Otherwise, generate error messages.
+  // Return true if parsing succeeds.
+  bool LoadTestExpectations(const std::string& data);
+  bool LoadTestExpectations(const base::FilePath& path);
+
+  // Query error messages from the last LoadTestExpectations() call.
+  const std::vector<std::string>& GetErrorMessages() const;
+
+  // Get the test expectation of a given test on a given bot.
+  int32 GetTestExpectation(const std::string& test_name,
+                           const GPUTestBotConfig& bot_config) const;
+
+  // Parse a list of config modifiers. If we have a valid entry with no
+  // conflicts, | config | stores it, and the function returns true.
+  bool ParseConfig(const std::string& config_data, GPUTestConfig* config);
+
+ private:
+  struct GPUTestExpectationEntry {
+    GPUTestExpectationEntry();
+
+    std::string test_name;
+    GPUTestConfig test_config;
+    int32 test_expectation;
+    size_t line_number;
+  };
+
+  // Parse a line of text. If we have a valid entry, save it; otherwise,
+  // generate error messages.
+  bool ParseLine(const std::string& line_data, size_t line_number);
+
+  // Update OS/GPUVendor/BuildType modifiers. May generate an error message.
+  bool UpdateTestConfig(
+      GPUTestConfig* config, int32 token, size_t line_number);
+
+  // Update GPUDeviceID modifier. May generate an error message.
+  bool UpdateTestConfig(GPUTestConfig* config,
+                        const std::string & gpu_device_id,
+                        size_t line_number);
+
+  // Check if two entries' config overlap with each other. May generate an
+  // error message.
+  bool DetectConflictsBetweenEntries();
+
+  // Save an error message, which can be queried later.
+  void PushErrorMessage(const std::string& message, size_t line_number);
+  void PushErrorMessage(const std::string& message,
+                        size_t entry1_line_number,
+                        size_t entry2_line_number);
+
+  std::vector<GPUTestExpectationEntry> entries_;
+  std::vector<std::string> error_messages_;
+};
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_TEST_EXPECTATIONS_PARSER_H_
+
diff --git a/gpu/config/gpu_test_expectations_parser_unittest.cc b/gpu/config/gpu_test_expectations_parser_unittest.cc
new file mode 100644
index 0000000..79c169c
--- /dev/null
+++ b/gpu/config/gpu_test_expectations_parser_unittest.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2012 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/logging.h"
+#include "gpu/config/gpu_test_expectations_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class GPUTestExpectationsParserTest : public testing::Test {
+ public:
+  GPUTestExpectationsParserTest() { }
+
+  virtual ~GPUTestExpectationsParserTest() { }
+
+  const GPUTestBotConfig& bot_config() const {
+    return bot_config_;
+  }
+
+ protected:
+  virtual void SetUp() {
+    bot_config_.set_os(GPUTestConfig::kOsWin7);
+    bot_config_.set_build_type(GPUTestConfig::kBuildTypeRelease);
+    bot_config_.AddGPUVendor(0x10de);
+    bot_config_.set_gpu_device_id(0x0640);
+    ASSERT_TRUE(bot_config_.IsValid());
+  }
+
+  virtual void TearDown() { }
+
+ private:
+  GPUTestBotConfig bot_config_;
+};
+
+TEST_F(GPUTestExpectationsParserTest, CommentOnly) {
+  const std::string text =
+      "  \n"
+      "// This is just some comment\n"
+      "";
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestPass,
+            parser.GetTestExpectation("some_test", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, ValidFullEntry) {
+  const std::string text =
+      "BUG12345 WIN7 RELEASE NVIDIA 0x0640 : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestFail,
+            parser.GetTestExpectation("MyTest", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, ValidPartialEntry) {
+  const std::string text =
+      "BUG12345 WIN NVIDIA : MyTest = TIMEOUT";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestTimeout,
+            parser.GetTestExpectation("MyTest", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, ValidUnrelatedOsEntry) {
+  const std::string text =
+      "BUG12345 LEOPARD : MyTest = TIMEOUT";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestPass,
+            parser.GetTestExpectation("MyTest", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, ValidUnrelatedTestEntry) {
+  const std::string text =
+      "BUG12345 WIN7 RELEASE NVIDIA 0x0640 : AnotherTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestPass,
+            parser.GetTestExpectation("MyTest", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, AllModifiers) {
+  const std::string text =
+      "BUG12345 XP VISTA WIN7 WIN8 LEOPARD SNOWLEOPARD LION MOUNTAINLION "
+      "LINUX CHROMEOS ANDROID "
+      "NVIDIA INTEL AMD VMWARE RELEASE DEBUG : MyTest = "
+      "PASS FAIL FLAKY TIMEOUT SKIP";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestPass |
+            GPUTestExpectationsParser::kGpuTestFail |
+            GPUTestExpectationsParser::kGpuTestFlaky |
+            GPUTestExpectationsParser::kGpuTestTimeout |
+            GPUTestExpectationsParser::kGpuTestSkip,
+            parser.GetTestExpectation("MyTest", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, DuplicateModifiers) {
+  const std::string text =
+      "BUG12345 WIN7 WIN7 RELEASE NVIDIA 0x0640 : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, AllModifiersLowerCase) {
+  const std::string text =
+      "BUG12345 xp vista win7 leopard snowleopard lion linux chromeos android "
+      "nvidia intel amd vmware release debug : MyTest = "
+      "pass fail flaky timeout skip";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestPass |
+            GPUTestExpectationsParser::kGpuTestFail |
+            GPUTestExpectationsParser::kGpuTestFlaky |
+            GPUTestExpectationsParser::kGpuTestTimeout |
+            GPUTestExpectationsParser::kGpuTestSkip,
+            parser.GetTestExpectation("MyTest", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, MissingColon) {
+  const std::string text =
+      "BUG12345 XP MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, MissingEqual) {
+  const std::string text =
+      "BUG12345 XP : MyTest FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, IllegalModifier) {
+  const std::string text =
+      "BUG12345 XP XXX : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, OsConflicts) {
+  const std::string text =
+      "BUG12345 XP WIN : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, InvalidModifierCombination) {
+  const std::string text =
+      "BUG12345 XP NVIDIA INTEL 0x0640 : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, BadGpuDeviceID) {
+  const std::string text =
+      "BUG12345 XP NVIDIA 0xU07X : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, MoreThanOneGpuDeviceID) {
+  const std::string text =
+      "BUG12345 XP NVIDIA 0x0640 0x0641 : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, MultipleEntriesConflicts) {
+  const std::string text =
+      "BUG12345 WIN7 RELEASE NVIDIA 0x0640 : MyTest = FAIL\n"
+      "BUG12345 WIN : MyTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_FALSE(parser.LoadTestExpectations(text));
+  EXPECT_NE(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, MultipleTests) {
+  const std::string text =
+      "BUG12345 WIN7 RELEASE NVIDIA 0x0640 : MyTest = FAIL\n"
+      "BUG12345 WIN : AnotherTest = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+}
+
+TEST_F(GPUTestExpectationsParserTest, ValidMultipleEntries) {
+  const std::string text =
+      "BUG12345 WIN7 RELEASE NVIDIA 0x0640 : MyTest = FAIL\n"
+      "BUG12345 LINUX : MyTest = TIMEOUT";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestFail,
+            parser.GetTestExpectation("MyTest", bot_config()));
+}
+
+TEST_F(GPUTestExpectationsParserTest, StarMatching) {
+  const std::string text =
+      "BUG12345 WIN7 RELEASE NVIDIA 0x0640 : MyTest* = FAIL";
+
+  GPUTestExpectationsParser parser;
+  EXPECT_TRUE(parser.LoadTestExpectations(text));
+  EXPECT_EQ(0u, parser.GetErrorMessages().size());
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestFail,
+            parser.GetTestExpectation("MyTest0", bot_config()));
+  EXPECT_EQ(GPUTestExpectationsParser::kGpuTestPass,
+            parser.GetTestExpectation("OtherTest", bot_config()));
+}
+
+}  // namespace gpu
+
diff --git a/gpu/config/gpu_util.cc b/gpu/config/gpu_util.cc
new file mode 100644
index 0000000..7fae88a
--- /dev/null
+++ b/gpu/config/gpu_util.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 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 "gpu/config/gpu_util.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/config/gpu_control_list_jsons.h"
+#include "gpu/config/gpu_driver_bug_list.h"
+#include "gpu/config/gpu_info_collector.h"
+#include "ui/gl/gl_switches.h"
+
+namespace gpu {
+
+namespace {
+
+// Combine the integers into a string, seperated by ','.
+std::string IntSetToString(const std::set<int>& list) {
+  std::string rt;
+  for (std::set<int>::const_iterator it = list.begin();
+       it != list.end(); ++it) {
+    if (!rt.empty())
+      rt += ",";
+    rt += base::IntToString(*it);
+  }
+  return rt;
+}
+
+}  // namespace anonymous
+
+GpuSwitchingOption StringToGpuSwitchingOption(
+    const std::string& switching_string) {
+  if (switching_string == switches::kGpuSwitchingOptionNameAutomatic)
+    return GPU_SWITCHING_OPTION_AUTOMATIC;
+  if (switching_string == switches::kGpuSwitchingOptionNameForceIntegrated)
+    return GPU_SWITCHING_OPTION_FORCE_INTEGRATED;
+  if (switching_string == switches::kGpuSwitchingOptionNameForceDiscrete)
+    return GPU_SWITCHING_OPTION_FORCE_DISCRETE;
+  return GPU_SWITCHING_OPTION_UNKNOWN;
+}
+
+std::string GpuSwitchingOptionToString(GpuSwitchingOption option) {
+  switch (option) {
+    case GPU_SWITCHING_OPTION_AUTOMATIC:
+      return switches::kGpuSwitchingOptionNameAutomatic;
+    case GPU_SWITCHING_OPTION_FORCE_INTEGRATED:
+      return switches::kGpuSwitchingOptionNameForceIntegrated;
+    case GPU_SWITCHING_OPTION_FORCE_DISCRETE:
+      return switches::kGpuSwitchingOptionNameForceDiscrete;
+    default:
+      return "unknown";
+  }
+}
+
+void MergeFeatureSets(std::set<int>* dst, const std::set<int>& src) {
+  DCHECK(dst);
+  if (src.empty())
+    return;
+  dst->insert(src.begin(), src.end());
+}
+
+void ApplyGpuDriverBugWorkarounds(CommandLine* command_line) {
+  GPUInfo gpu_info;
+  CollectBasicGraphicsInfo(&gpu_info);
+
+  GpuDriverBugList* list = GpuDriverBugList::Create();
+  list->LoadList("0", kGpuDriverBugListJson,
+                 GpuControlList::kCurrentOsOnly);
+  std::set<int> workarounds = list->MakeDecision(
+      GpuControlList::kOsAny, std::string(), gpu_info);
+  if (!workarounds.empty()) {
+    command_line->AppendSwitchASCII(switches::kGpuDriverBugWorkarounds,
+                                    IntSetToString(workarounds));
+  }
+}
+
+}  // namespace gpu
diff --git a/gpu/config/gpu_util.h b/gpu/config/gpu_util.h
new file mode 100644
index 0000000..6d6800e
--- /dev/null
+++ b/gpu/config/gpu_util.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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 GPU_CONFIG_GPU_UTIL_H_
+#define GPU_CONFIG_GPU_UTIL_H_
+
+#include <set>
+#include <string>
+
+#include "build/build_config.h"
+#include "gpu/config/gpu_switching_option.h"
+#include "gpu/gpu_export.h"
+
+class CommandLine;
+
+namespace gpu {
+
+// Maps string to GpuSwitchingOption; returns GPU_SWITCHING_UNKNOWN if an
+// unknown name is input (case-sensitive).
+GPU_EXPORT GpuSwitchingOption StringToGpuSwitchingOption(
+    const std::string& switching_string);
+
+// Gets a string version of a GpuSwitchingOption.
+GPU_EXPORT std::string GpuSwitchingOptionToString(GpuSwitchingOption option);
+
+// Merge features in src into dst.
+GPU_EXPORT void MergeFeatureSets(
+    std::set<int>* dst, const std::set<int>& src);
+
+// Collect basic GPUInfo, compute the driver bug workarounds for the current
+// system, and append the |command_line|.
+GPU_EXPORT void ApplyGpuDriverBugWorkarounds(CommandLine* command_line);
+
+}  // namespace gpu
+
+#endif  // GPU_CONFIG_GPU_UTIL_H_
+
diff --git a/gpu/config/gpu_util_unittest.cc b/gpu/config/gpu_util_unittest.cc
new file mode 100644
index 0000000..f3e3f8f
--- /dev/null
+++ b/gpu/config/gpu_util_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 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 "gpu/config/gpu_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_switches.h"
+
+namespace gpu {
+
+TEST(GpuUtilTest, GpuSwitchingOptionFromString) {
+  // Test StringToGpuSwitchingOption.
+  EXPECT_EQ(StringToGpuSwitchingOption(
+                switches::kGpuSwitchingOptionNameAutomatic),
+            GPU_SWITCHING_OPTION_AUTOMATIC);
+  EXPECT_EQ(StringToGpuSwitchingOption(
+                switches::kGpuSwitchingOptionNameForceDiscrete),
+            GPU_SWITCHING_OPTION_FORCE_DISCRETE);
+  EXPECT_EQ(StringToGpuSwitchingOption(
+                switches::kGpuSwitchingOptionNameForceIntegrated),
+            GPU_SWITCHING_OPTION_FORCE_INTEGRATED);
+  EXPECT_EQ(StringToGpuSwitchingOption("xxx"), GPU_SWITCHING_OPTION_UNKNOWN);
+}
+
+TEST(GpuUtilTest, GpuSwitchingOptionToString) {
+  // Test GpuSwitchingOptionToString.
+  EXPECT_STREQ(
+      GpuSwitchingOptionToString(GPU_SWITCHING_OPTION_AUTOMATIC).c_str(),
+      switches::kGpuSwitchingOptionNameAutomatic);
+  EXPECT_STREQ(
+      GpuSwitchingOptionToString(GPU_SWITCHING_OPTION_FORCE_DISCRETE).c_str(),
+      switches::kGpuSwitchingOptionNameForceDiscrete);
+  EXPECT_STREQ(
+      GpuSwitchingOptionToString(GPU_SWITCHING_OPTION_FORCE_INTEGRATED).c_str(),
+      switches::kGpuSwitchingOptionNameForceIntegrated);
+}
+
+TEST(GpuUtilTest, MergeFeatureSets) {
+  {
+    // Merge two empty sets.
+    std::set<int> src;
+    std::set<int> dst;
+    EXPECT_TRUE(dst.empty());
+    MergeFeatureSets(&dst, src);
+    EXPECT_TRUE(dst.empty());
+  }
+  {
+    // Merge an empty set into a set with elements.
+    std::set<int> src;
+    std::set<int> dst;
+    dst.insert(1);
+    EXPECT_EQ(1u, dst.size());
+    MergeFeatureSets(&dst, src);
+    EXPECT_EQ(1u, dst.size());
+  }
+  {
+    // Merge two sets where the source elements are already in the target set.
+    std::set<int> src;
+    std::set<int> dst;
+    src.insert(1);
+    dst.insert(1);
+    EXPECT_EQ(1u, dst.size());
+    MergeFeatureSets(&dst, src);
+    EXPECT_EQ(1u, dst.size());
+  }
+  {
+    // Merge two sets with different elements.
+    std::set<int> src;
+    std::set<int> dst;
+    src.insert(1);
+    dst.insert(2);
+    EXPECT_EQ(1u, dst.size());
+    MergeFeatureSets(&dst, src);
+    EXPECT_EQ(2u, dst.size());
+  }
+}
+
+}  // namespace gpu
diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc
new file mode 100644
index 0000000..9621100
--- /dev/null
+++ b/gpu/config/software_rendering_list_json.cc
@@ -0,0 +1,1116 @@
+// Copyright (c) 2013 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.
+
+// Determines whether certain gpu-related features are blacklisted or not.
+// A valid software_rendering_list.json file are in the format of
+// {
+//   "version": "x.y",
+//   "entries": [
+//     { // entry 1
+//     },
+//     ...
+//     { // entry n
+//     }
+//   ]
+// }
+//
+// Each entry contains the following fields (fields are optional unless
+// specifically described as mandatory below):
+// 1. "id" is an integer.  0 is reserved.  This field is mandatory.
+// 2. "os" contains "type" and an optional "version". "type" could be "macosx",
+//    "linux", "win", "chromeos", or "any".  "any" is the same as not specifying
+//    "os".
+//    "version" is a VERSION structure (defined below).
+// 3. "vendor_id" is a string.  0 is reserved.
+// 4. "device_id" is an array of strings.  0 is reserved.
+// 5. "multi_gpu_style" is a string, valid values include "optimus", and
+//    "amd_switchable".
+// 6. "multi_gpu_category" is a string, valid values include "any", "primary",
+//    and "secondary".  If unspecified, the default value is "primary".
+// 7. "driver_vendor" is a STRING structure (defined below).
+// 8. "driver_version" is a VERSION structure (defined below).
+// 9. "driver_date" is a VERSION structure (defined below).
+//    The version is interpreted as "year.month.day".
+// 10. "gl_vendor" is a STRING structure (defined below).
+// 11. "gl_renderer" is a STRING structure (defined below).
+// 12. "gl_extensions" is a STRING structure (defined below).
+// 13. "perf_graphics" is a FLOAT structure (defined below).
+// 14. "perf_gaming" is a FLOAT structure (defined below).
+// 15. "perf_overall" is a FLOAT structure (defined below).
+// 16. "machine_model" contais "name" and an optional "version".  "name" is a
+//     STRING structure and "version" is a VERSION structure (defined below).
+// 17. "gpu_count" is a INT structure (defined below).
+// 18  "cpu_info" is a STRING structure (defined below).
+// 19. "exceptions" is a list of entries.
+// 20. "features" is a list of gpu feature strings, valid values include
+//     "accelerated_2d_canvas", "accelerated_compositing", "webgl",
+//     "multisampling", "flash_3d", "flash_stage3d", "texture_sharing",
+//     "accelerated_video", "accelerated_video_decode", "panel_fitting",
+//     "force_compositing_mode", and "all".
+//     This field is mandatory.
+// 21. "description" has the description of the entry.
+// 22. "webkit_bugs" is an array of associated webkit bug numbers.
+// 23. "cr_bugs" is an array of associated webkit bug numbers.
+// 24. "browser_version" is a VERSION structure (defined below).  If this
+//     condition is not satisfied, the entry will be ignored.  If it is not
+//     present, then the entry applies to all versions of the browser.
+// 25. "disabled" is a boolean. If it is present, the entry will be skipped.
+//     This can not be used in exceptions.
+//
+// VERSION includes "op", "style", "number", and "number2".  "op" can be any of
+// the following values: "=", "<", "<=", ">", ">=", "any", "between".  "style"
+// is optional and can be "lexical" or "numerical"; if it's not specified, it
+// defaults to "numerical".  "number2" is only used if "op" is "between".
+// "between" is "number <= * <= number2".
+// "number" is used for all "op" values except "any". "number" and "number2"
+// are in the format of x, x.x, x.x.x, etc.
+// Only "driver_version" supports lexical style if the format is major.minor;
+// in that case, major is still numerical, but minor is lexical. 
+//
+// STRING includes "op" and "value".  "op" can be any of the following values:
+// "contains", "beginwith", "endwith", "=".  "value" is a string.
+//
+// FLOAT includes "op" "value", and "value2".  "op" can be any of the
+// following values: "=", "<", "<=", ">", ">=", "any", "between".  "value2" is
+// only used if "op" is "between".  "value" is used for all "op" values except
+// "any". "value" and "value2" are valid float numbers.
+// INT is very much like FLOAT, except that the values need to be integers.
+
+#include "gpu/config/gpu_control_list_jsons.h"
+
+#define LONG_STRING_CONST(...) #__VA_ARGS__
+
+namespace gpu {
+
+const char kSoftwareRenderingListJson[] = LONG_STRING_CONST(
+
+{
+  "name": "software rendering list",
+  // Please update the version number whenever you change this file.
+  "version": "6.0",
+  "entries": [
+    {
+      "id": 1,
+      "description": "ATI Radeon X1900 is not compatible with WebGL on the Mac.",
+      "webkit_bugs": [47028],
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x1002",
+      "device_id": ["0x7249"],
+      "features": [
+        "webgl",
+        "flash_3d",
+        "flash_stage3d"
+      ]
+    },
+    {
+      "id": 3,
+      "description": "GL driver is software rendered. Accelerated compositing is disabled.",
+      "cr_bugs": [59302],
+      "os": {
+        "type": "linux"
+      },
+      "gl_renderer": {
+        "op": "contains",
+        "value": "software"
+      },
+      "features": [
+        "accelerated_compositing"
+      ]
+    },
+    {
+      "id": 4,
+      "description": "The Intel Mobile 945 Express family of chipsets is not compatible with WebGL.",
+      "os": {
+        "type": "any"
+      },
+      "vendor_id": "0x8086",
+      "device_id": ["0x27AE"],
+      "features": [
+        "webgl",
+        "flash_3d",
+        "flash_stage3d"
+      ]
+    },
+    {
+      "id": 5,
+      "description": "ATI/AMD cards with older or third-party drivers in Linux are crash-prone.",
+      "cr_bugs": [71381, 76428, 73910, 101225, 136240],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x1002",
+      "exceptions": [
+        {
+          "driver_vendor": {
+            "op": "contains",
+            "value": "AMD"
+          },
+          "driver_version": {
+            "op": ">=",
+            "style": "lexical",
+            "number": "8.98"
+          }
+        }
+      ],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 8,
+      "description": "NVIDIA GeForce FX Go5200 is assumed to be buggy.",
+      "cr_bugs": [72938],
+      "os": {
+        "type": "any"
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x0324"],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 10,
+      "description": "NVIDIA GeForce 7300 GT on Mac does not support WebGL.",
+      "cr_bugs": [73794],
+      "os": {
+        "type": "macosx"
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x0393"],
+      "features": [
+        "webgl",
+        "flash_3d",
+        "flash_stage3d"
+      ]
+    },
+    {
+      "id": 12,
+      "description": "Drivers older than 2009-01 on Windows are possibly unreliable.",
+      "cr_bugs": [72979, 89802],
+      "os": {
+        "type": "win"
+      },
+      "driver_date": {
+        "op": "<",
+        "number": "2009.1"
+      },
+      "exceptions": [
+        {
+          "vendor_id": "0x8086",
+          "device_id": ["0x29a2"],
+          "driver_version": {
+            "op": ">=",
+            "number": "7.15.10.1624"
+          }
+        }
+      ],
+      "features": [
+        "accelerated_2d_canvas",
+        "accelerated_video",
+        "accelerated_video_decode",
+        "3d_css",
+        "multisampling",
+        "flash_3d",
+        "force_compositing_mode"
+      ]
+    },
+    {
+      "id": 13,
+      "description": "ATI drivers older than 10.6 on Windows XP are possibly unreliable.",
+      "cr_bugs": [74212],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "vendor_id": "0x1002",
+      "driver_version": {
+        "op": "<",
+        "number": "8.741"
+      },
+      "features": [
+        "accelerated_video",
+        "accelerated_video_decode",
+        "3d_css",
+        "multisampling",
+        "flash_3d",
+        "force_compositing_mode"
+      ]
+    },
+    {
+      "id": 14,
+      "description": "NVIDIA drivers older than 257.21 on Windows XP are possibly unreliable.",
+      "cr_bugs": [74212],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "vendor_id": "0x10de",
+      "driver_version": {
+        "op": "<",
+        "number": "6.14.12.5721"
+      },
+      "features": [
+        "accelerated_video",
+        "accelerated_video_decode",
+        "3d_css",
+        "multisampling",
+        "flash_3d",
+        "force_compositing_mode"
+      ]
+    },
+    {
+      "id": 15,
+      "description": "Intel drivers older than 14.42.7.5294 on Windows XP are possibly unreliable.",
+      "cr_bugs": [74212],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "vendor_id": "0x8086",
+      "driver_version": {
+        "op": "<",
+        "number": "6.14.10.5294"
+      },
+      "features": [
+        "accelerated_video",
+        "accelerated_video_decode",
+        "3d_css",
+        "multisampling",
+        "flash_3d",
+        "force_compositing_mode"
+      ]
+    },
+    {
+      "id": 16,
+      "description": "Multisampling is buggy in ATI cards on older MacOSX.",
+      "cr_bugs": [67752, 83153],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "<",
+          "number": "10.7.2"
+        }
+      },
+      "vendor_id": "0x1002",
+      "features": [
+        "multisampling"
+      ]
+    },
+    {
+      "id": 17,
+      "description": "Intel mesa drivers are crash-prone.",
+      "cr_bugs": [76703, 164555],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x8086",
+      "exceptions": [
+        {
+          "device_id": ["0x0102", "0x0106", "0x0112", "0x0116", "0x0122", "0x0126", "0x010a", "0x0152", "0x0156", "0x015a", "0x0162", "0x0166"],
+          "driver_version": {
+            "op": ">=",
+            "number": "8.0"
+          }
+        },
+        {
+          "device_id": ["0xa001", "0xa002", "0xa011", "0xa012", "0x29a2", "0x2992", "0x2982", "0x2972", "0x2a12", "0x2a42", "0x2e02", "0x2e12", "0x2e22", "0x2e32", "0x2e42", "0x2e92"],
+          "driver_version": {
+            "op": ">",
+            "number": "8.0.2"
+          }
+        },
+        {
+          "device_id": ["0x0042", "0x0046"],
+          "driver_version": {
+            "op": ">=",
+            "number": "8.0.2"
+          }
+        },
+        {
+          "device_id": ["0x2a02"],
+          "driver_version": {
+            "op": ">=",
+            "number": "9.1"
+          }
+        }
+      ],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 18,
+      "description": "NVIDIA Quadro FX 1500 is buggy.",
+      "cr_bugs": [84701],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x029e"],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 19,
+      "description": "GPU acceleration is no longer supported in Leopard.",
+      "cr_bugs": [87157, 130495],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "=",
+          "number": "10.5"
+        }
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 23,
+      "description": "Mesa drivers in linux older than 7.11 are assumed to be buggy.",
+      "os": {
+        "type": "linux"
+      },
+      "driver_vendor": {
+        "op": "=",
+        "value": "Mesa"
+      },
+      "driver_version": {
+        "op": "<",
+        "number": "7.11"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 24,
+      "description": "Accelerated 2d canvas is unstable in Linux at the moment.",
+      "os": {
+        "type": "linux"
+      },
+      "features": [
+        "accelerated_2d_canvas"
+      ]
+    },
+    {
+      "id": 27,
+      "description": "ATI/AMD cards with older drivers in Linux are crash-prone.",
+      "cr_bugs": [95934, 94973, 136240],
+      "os": {
+        "type": "linux"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "ATI"
+      },
+      "exceptions": [
+        {
+          "driver_vendor": {
+            "op": "contains",
+            "value": "AMD"
+          },
+          "driver_version": {
+            "op": ">=",
+            "style": "lexical",
+            "number": "8.98"
+          }
+        }
+      ],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 28,
+      "description": "ATI/AMD cards with third-party drivers in Linux are crash-prone.",
+      "cr_bugs": [95934, 94973],
+      "os": {
+        "type": "linux"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "X.Org"
+      },
+      "gl_renderer": {
+        "op": "contains",
+        "value": "AMD"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 29,
+      "description": "ATI/AMD cards with third-party drivers in Linux are crash-prone.",
+      "cr_bugs": [95934, 94973],
+      "os": {
+        "type": "linux"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "X.Org"
+      },
+      "gl_renderer": {
+        "op": "contains",
+        "value": "ATI"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 30,
+      "description": "NVIDIA cards with nouveau drivers in Linux are crash-prone.",
+      "cr_bugs": [94103],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x10de",
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "nouveau"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 32,
+      "description": "Accelerated 2d canvas is disabled on Windows systems with low perf stats.",
+      "cr_bugs": [116350, 151500],
+      "os": {
+        "type": "win"
+      },
+      "perf_overall": {
+        "op": "<",
+        "value": "3.5"
+      },
+      "exceptions": [
+        {
+          "perf_gaming": {
+            "op": ">",
+            "value": "3.5"
+          }
+        },
+        {
+          "cpu_info": {
+            "op": "contains",
+            "value": "Atom"
+          }
+        }
+      ],
+      "features": [
+        "accelerated_2d_canvas"
+      ]
+    },
+    {
+      "id": 33,
+      "description": "Multisampling is buggy in Intel IvyBridge.",
+      "cr_bugs": [116370],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x8086",
+      "device_id": ["0x0152", "0x0156", "0x015a", "0x0162", "0x0166"],
+      "features": [
+          "multisampling"
+      ]
+    },
+    {
+      "id": 34,
+      "description": "S3 Trio (used in Virtual PC) is not compatible.",
+      "cr_bugs": [119948],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x5333",
+      "device_id": ["0x8811"],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 35,
+      "description": "Stage3D is not supported on Linux.",
+      "cr_bugs": [129848],
+      "os": {
+        "type": "linux"
+      },
+      "features": [
+        "flash_stage3d"
+      ]
+    },
+    {
+      "id": 37,
+      "description": "Drivers are unreliable for Optimus on Linux.",
+      "cr_bugs": [131308],
+      "os": {
+        "type": "linux"
+      },
+      "multi_gpu_style": "optimus",
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 38,
+      "description": "Accelerated 2D canvas is unstable for NVidia GeForce 9400M on Lion.",
+      "cr_bugs": [130495],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "=",
+          "number": "10.7"
+        }
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x0863"],
+      "features": [
+        "accelerated_2d_canvas"
+      ]
+    },
+    {
+      "id": 41,
+      "description": "Disable 3D (but not Stage3D) in Flash on XP",
+      "cr_bugs": [134885],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "features": [
+        "flash_3d"
+      ]
+    },
+    {
+      "id": 42,
+      "description": "AMD Radeon HD 6490M on Snow Leopard is buggy.",
+      "cr_bugs": [137307],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "=",
+          "number": "10.6"
+        }
+      },
+      "vendor_id": "0x1002",
+      "device_id": ["0x6760"],
+      "features": [
+        "webgl"
+      ]
+    },
+    {
+      "id": 43,
+      "description": "Intel driver version 8.15.10.1749 has problems sharing textures.",
+      "cr_bugs": [133924],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x8086",
+      "driver_version": {
+        "op": "=",
+        "number": "8.15.10.1749"
+      },
+      "features": [
+        "texture_sharing"
+      ]
+    },
+    {
+      "id": 44,
+      "description": "Intel HD 4000 causes kernel panic on Lion.",
+      "cr_bugs": [134015],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "between",
+          "number": "10.7.0",
+          "number2": "10.7.4"
+        }
+      },
+      "vendor_id": "0x8086",
+      "device_id": ["0x0166"],
+      "multi_gpu_category": "any",
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 45,
+      "description": "Parallels drivers older than 7 are buggy.",
+      "cr_bugs": [138105],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x1ab8",
+      "driver_version": {
+        "op": "<",
+        "number": "7"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 46,
+      "description": "ATI FireMV 2400 cards on Windows are buggy.",
+      "cr_bugs": [124152],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x1002",
+      "device_id": ["0x3151"],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 47,
+      "description": "NVIDIA linux drivers older than 295.* are assumed to be buggy.",
+      "cr_bugs": [78497],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x10de",
+      "driver_vendor": {
+        "op": "=",
+        "value": "NVIDIA"
+      },
+      "driver_version": {
+        "op": "<",
+        "number": "295"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 48,
+      // Please keep in sync with content/test/content_browser_test.cc.
+      "description": "Accelerated video decode is unavailable on Mac and Linux.",
+      "cr_bugs": [137247, 133828],
+      "exceptions": [
+        {
+          "os": {
+            "type": "chromeos"
+          }
+        },
+        {
+          "os": {
+            "type": "win"
+          }
+        }
+      ],
+      "features": [
+        "accelerated_video_decode"
+      ]
+    },
+    {
+      "id": 49,
+      "description": "NVidia GeForce GT 650M can cause the system to hang with flash 3D.",
+      "cr_bugs": [140175],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "between",
+          "number": "10.8.0",
+          "number2": "10.8.1"
+        }
+      },
+      "multi_gpu_style": "optimus",
+      "vendor_id": "0x10de",
+      "device_id": ["0x0fd5"],
+      "features": [
+        "flash_3d",
+        "flash_stage3d"
+      ]
+    },
+    {
+      "id": 50,
+      "description": "Disable VMware software renderer.",
+      "cr_bugs": [145531],
+      "os": {
+        "type": "linux"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "VMware"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 51,
+      "description": "NVIDIA drivers 6.14.11.9621 is buggy on Windows XP.",
+      "cr_bugs": [152096],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "vendor_id": "0x10de",
+      "driver_version": {
+        "op": "=",
+        "number": "6.14.11.9621"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 52,
+      "description": "NVIDIA drivers 6.14.11.8267 is buggy on Windows XP.",
+      "cr_bugs": [152096],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "vendor_id": "0x10de",
+      "driver_version": {
+        "op": "=",
+        "number": "6.14.11.8267"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 53,
+      "description": "The Intel GMA500 is too slow for Stage3D.",
+      "cr_bugs": [152096],
+      "vendor_id": "0x8086",
+      "device_id": ["0x8108", "0x8109"],
+      "features": [
+        "flash_stage3d"
+      ]
+    },
+    {
+      "id": 55,
+      "description": "Drivers older than 2007-01 on Windows are assumed to be buggy.",
+      "cr_bugs": [72979, 89802],
+      "os": {
+        "type": "win"
+      },
+      "driver_date": {
+        "op": "<",
+        "number": "2007.1"
+      },
+      "exceptions": [
+        {
+          "vendor_id": "0x8086",
+          "device_id": ["0x29a2"],
+          "driver_version": {
+            "op": ">=",
+            "number": "7.15.10.1624"
+          }
+        }
+      ],
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 56,
+      "description": "NVIDIA linux drivers are unstable when using multiple Open GL contexts and with low memory.",
+      "cr_bugs": [145600],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x10de",
+      "driver_vendor": {
+        "op": "=",
+        "value": "NVIDIA"
+      },
+      "features": [
+        "accelerated_video",
+        "accelerated_video_decode",
+        "flash_3d",
+        "flash_stage3d"
+      ]
+    },
+    {
+      "id": 57,
+      "description": "Enable panel fitting capability on ChromeOS only on IVB and SNB Graphics Controllers.",
+      "exceptions": [
+        {
+          "os": {
+            "type": "chromeos"
+          },
+          "vendor_id": "0x8086",
+          "device_id": ["0x0106", "0x0116", "0x0166"]
+        }
+      ],
+      "features": [
+        "panel_fitting"
+      ]
+    },
+    {
+      "id": 59,
+      "description": "NVidia driver 8.15.11.8593 is crashy on Windows.",
+      "cr_bugs": [155749],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x10de",
+      "driver_version": {
+        "op": "=",
+        "number": "8.15.11.8593"
+      },
+      "features": [
+        "accelerated_video_decode"
+      ]
+    },
+    {
+      "id": 60,
+      "description": "Multisampling is buggy on Mac with NVIDIA gpu prior to 10.8.3.",
+      "cr_bugs": [137303],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "<",
+          "number": "10.8.3"
+        }
+      },
+      "vendor_id": "0x10de",
+      "features": [
+        "multisampling"
+      ]
+    },
+    {
+      "id": 61,
+      "description": "Multisampling is buggy on Mac with Intel gpu prior to 10.8.3.",
+      "cr_bugs": [137303],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "<",
+          "number": "10.8.3"
+        }
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "multisampling"
+      ]
+    },
+    {
+      "id": 62,
+      "description": "Accelerated 2D canvas buggy on old Qualcomm Adreno.",
+      "cr_bugs": [161575],
+      "os": {
+        "type": "android"
+      },
+      "gl_renderer": {
+        "op": "contains",
+        "value": "Adreno"
+      },
+      "driver_version": {
+        "op": "<",
+        "number": "4.1"
+      },
+      "features": [
+        "accelerated_2d_canvas"
+      ]
+    },
+    {
+      "id": 63,
+      "description": "Multisampling is buggy on Mac with AMD gpu prior to 10.8.3.",
+      "cr_bugs": [162466],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "<",
+          "number": "10.8.3"
+        }
+      },
+      "vendor_id": "0x1002",
+      "features": [
+        "multisampling"
+      ]
+    },
+    {
+      "id": 64,
+      "description": "Hardware video decode is only supported in win7+.",
+      "cr_bugs": [159458],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "<",
+          "number": "6.1"
+        }
+      },
+      "features": [
+        "accelerated_video_decode"
+      ]
+    },
+    {
+      "id": 65,
+      "description": "Force compositing mode is unstable in Win Vista.",
+      "cr_bugs": [170421],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "6.0"
+        }
+      },
+      "features": [
+        "force_compositing_mode"
+      ]
+    },
+    {
+      "id": 66,
+      "description": "Force compositing mode is unstable in MacOSX earlier than 10.8.",
+      "cr_bugs": [174101],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "<",
+          "number": "10.8"
+        }
+      },
+      "features": [
+        "force_compositing_mode"
+      ]
+    },
+    {
+      "id": 67,
+      "description": "Accelerated 2D Canvas is not supported on WinXP.",
+      "cr_bugs": [175149],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "features": [
+        "accelerated_2d_canvas"
+      ]
+    },
+    {
+      "id": 68,
+      "description": "VMware Fusion 4 has corrupt rendering with Win Vista+.",
+      "cr_bugs": [169470],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": ">=",
+          "number": "6.0"
+        }
+      },
+      "vendor_id": "0x15ad",
+      "driver_version": {
+        "op": "<=",
+        "number": "7.14.1.1134"
+      },
+      "features": [
+        "all"
+      ]
+    },
+    {
+      "id": 69,
+      "description": "NVIDIA driver 8.17.11.9621 is buggy with Stage3D baseline mode.",
+      "cr_bugs": [172771],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x10de",
+      "driver_version": {
+        "op": "=",
+        "number": "8.17.11.9621"
+      },
+      "features": [
+        "flash_stage3d_baseline"
+      ]
+    },
+    {
+      "id": 70,
+      "description": "NVIDIA driver 8.17.11.8267 is buggy with Stage3D baseline mode.",
+      "cr_bugs": [172771],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x10de",
+      "driver_version": {
+        "op": "=",
+        "number": "8.17.11.8267"
+      },
+      "features": [
+        "flash_stage3d_baseline"
+      ]
+    },
+    {
+      "id": 71,
+      "description": "All Intel drivers before 8.15.10.2021 are buggy with Stage3D baseline mode.",
+      "cr_bugs": [172771],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x8086",
+      "driver_version": {
+        "op": "<",
+        "number": "8.15.10.2021"
+      },
+      "features": [
+        "flash_stage3d_baseline"
+      ]
+    },
+    {
+      "id": 72,
+      "description": "NVIDIA GeForce 6200 LE is buggy with WebGL.",
+      "cr_bugs": [232529],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x0163"],
+      "features": [
+        "webgl"
+      ]
+    },
+    {
+      "id": 73,
+      "description": "WebGL is buggy with the NVIDIA GeForce GT 330M, 9400, and 9400M on MacOSX earlier than 10.8",
+      "cr_bugs": [233523],
+      "os": {
+        "type": "macosx",
+        "version": {
+          "op": "<",
+          "number": "10.8"
+        }
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x0a29", "0x0861", "0x0863"],
+      "features": [
+        "webgl"
+      ]
+    }
+  ]
+}
+
+);  // LONG_STRING_CONST macro
+
+}  // namespace gpu
+
diff --git a/gpu/disk_cache_proto.target.darwin-arm.mk b/gpu/disk_cache_proto.target.darwin-arm.mk
index 5ade799..2f140c9 100644
--- a/gpu/disk_cache_proto.target.darwin-arm.mk
+++ b/gpu/disk_cache_proto.target.darwin-arm.mk
@@ -17,14 +17,14 @@
 ### Generated for rule "gpu_gpu_gyp_disk_cache_proto_target_genproto":
 # "{'inputs': ['../tools/protoc_wrapper/protoc_wrapper.py', '$(gyp_shared_intermediate_dir)/protoc'], 'msvs_cygwin_shell': '0', 'extension': 'proto', 'outputs': ['$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/%(INPUT_ROOT)s_pb2.py', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.cc', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h'], 'rule_name': 'genproto', 'rule_sources': ['command_buffer/service/disk_cache_proto.proto'], 'action': ['python', '../tools/protoc_wrapper/protoc_wrapper.py', '--include', '', '--protobuf', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h', '--proto-in-dir', 'command_buffer/service', '--proto-in-file', '%(INPUT_ROOT)s$(suffix $<)', '--use-system-protobuf=0', '--', '$(gyp_shared_intermediate_dir)/protoc', '--cpp_out', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service', '--python_out', '$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service'], 'message': 'Generating C++ and Python code from $(RULE_SOURCES)', 'process_outputs_as_sources': '1'}":
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: $(LOCAL_PATH)/gpu/command_buffer/service/disk_cache_proto.proto $(LOCAL_PATH)/tools/protoc_wrapper/protoc_wrapper.py $(gyp_shared_intermediate_dir)/protoc $(GYP_TARGET_DEPENDENCIES)
 	mkdir -p $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service $(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service; cd $(gyp_local_path)/gpu; python ../tools/protoc_wrapper/protoc_wrapper.py --include "" --protobuf "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h" --proto-in-dir command_buffer/service --proto-in-file "disk_cache_proto$(suffix $<)" "--use-system-protobuf=0" -- "$(gyp_shared_intermediate_dir)/protoc" --cpp_out "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service" --python_out "$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service"
 
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
 .PHONY: gpu_disk_cache_proto_gyp_rule_trigger
 gpu_disk_cache_proto_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
 
@@ -94,6 +94,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -122,9 +123,9 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/disk_cache_proto.target.darwin-x86.mk b/gpu/disk_cache_proto.target.darwin-x86.mk
index d3c2c65..5117015 100644
--- a/gpu/disk_cache_proto.target.darwin-x86.mk
+++ b/gpu/disk_cache_proto.target.darwin-x86.mk
@@ -17,14 +17,14 @@
 ### Generated for rule "gpu_gpu_gyp_disk_cache_proto_target_genproto":
 # "{'inputs': ['../tools/protoc_wrapper/protoc_wrapper.py', '$(gyp_shared_intermediate_dir)/protoc'], 'msvs_cygwin_shell': '0', 'extension': 'proto', 'outputs': ['$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/%(INPUT_ROOT)s_pb2.py', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.cc', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h'], 'rule_name': 'genproto', 'rule_sources': ['command_buffer/service/disk_cache_proto.proto'], 'action': ['python', '../tools/protoc_wrapper/protoc_wrapper.py', '--include', '', '--protobuf', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h', '--proto-in-dir', 'command_buffer/service', '--proto-in-file', '%(INPUT_ROOT)s$(suffix $<)', '--use-system-protobuf=0', '--', '$(gyp_shared_intermediate_dir)/protoc', '--cpp_out', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service', '--python_out', '$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service'], 'message': 'Generating C++ and Python code from $(RULE_SOURCES)', 'process_outputs_as_sources': '1'}":
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: $(LOCAL_PATH)/gpu/command_buffer/service/disk_cache_proto.proto $(LOCAL_PATH)/tools/protoc_wrapper/protoc_wrapper.py $(gyp_shared_intermediate_dir)/protoc $(GYP_TARGET_DEPENDENCIES)
 	mkdir -p $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service $(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service; cd $(gyp_local_path)/gpu; python ../tools/protoc_wrapper/protoc_wrapper.py --include "" --protobuf "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h" --proto-in-dir command_buffer/service --proto-in-file "disk_cache_proto$(suffix $<)" "--use-system-protobuf=0" -- "$(gyp_shared_intermediate_dir)/protoc" --cpp_out "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service" --python_out "$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service"
 
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
 .PHONY: gpu_disk_cache_proto_gyp_rule_trigger
 gpu_disk_cache_proto_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
 
@@ -96,6 +96,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -124,9 +125,9 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/disk_cache_proto.target.linux-arm.mk b/gpu/disk_cache_proto.target.linux-arm.mk
index 5ade799..2f140c9 100644
--- a/gpu/disk_cache_proto.target.linux-arm.mk
+++ b/gpu/disk_cache_proto.target.linux-arm.mk
@@ -17,14 +17,14 @@
 ### Generated for rule "gpu_gpu_gyp_disk_cache_proto_target_genproto":
 # "{'inputs': ['../tools/protoc_wrapper/protoc_wrapper.py', '$(gyp_shared_intermediate_dir)/protoc'], 'msvs_cygwin_shell': '0', 'extension': 'proto', 'outputs': ['$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/%(INPUT_ROOT)s_pb2.py', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.cc', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h'], 'rule_name': 'genproto', 'rule_sources': ['command_buffer/service/disk_cache_proto.proto'], 'action': ['python', '../tools/protoc_wrapper/protoc_wrapper.py', '--include', '', '--protobuf', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h', '--proto-in-dir', 'command_buffer/service', '--proto-in-file', '%(INPUT_ROOT)s$(suffix $<)', '--use-system-protobuf=0', '--', '$(gyp_shared_intermediate_dir)/protoc', '--cpp_out', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service', '--python_out', '$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service'], 'message': 'Generating C++ and Python code from $(RULE_SOURCES)', 'process_outputs_as_sources': '1'}":
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: $(LOCAL_PATH)/gpu/command_buffer/service/disk_cache_proto.proto $(LOCAL_PATH)/tools/protoc_wrapper/protoc_wrapper.py $(gyp_shared_intermediate_dir)/protoc $(GYP_TARGET_DEPENDENCIES)
 	mkdir -p $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service $(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service; cd $(gyp_local_path)/gpu; python ../tools/protoc_wrapper/protoc_wrapper.py --include "" --protobuf "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h" --proto-in-dir command_buffer/service --proto-in-file "disk_cache_proto$(suffix $<)" "--use-system-protobuf=0" -- "$(gyp_shared_intermediate_dir)/protoc" --cpp_out "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service" --python_out "$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service"
 
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
 .PHONY: gpu_disk_cache_proto_gyp_rule_trigger
 gpu_disk_cache_proto_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
 
@@ -94,6 +94,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -122,9 +123,9 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/disk_cache_proto.target.linux-x86.mk b/gpu/disk_cache_proto.target.linux-x86.mk
index d3c2c65..5117015 100644
--- a/gpu/disk_cache_proto.target.linux-x86.mk
+++ b/gpu/disk_cache_proto.target.linux-x86.mk
@@ -17,14 +17,14 @@
 ### Generated for rule "gpu_gpu_gyp_disk_cache_proto_target_genproto":
 # "{'inputs': ['../tools/protoc_wrapper/protoc_wrapper.py', '$(gyp_shared_intermediate_dir)/protoc'], 'msvs_cygwin_shell': '0', 'extension': 'proto', 'outputs': ['$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/%(INPUT_ROOT)s_pb2.py', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.cc', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h'], 'rule_name': 'genproto', 'rule_sources': ['command_buffer/service/disk_cache_proto.proto'], 'action': ['python', '../tools/protoc_wrapper/protoc_wrapper.py', '--include', '', '--protobuf', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/%(INPUT_ROOT)s.pb.h', '--proto-in-dir', 'command_buffer/service', '--proto-in-file', '%(INPUT_ROOT)s$(suffix $<)', '--use-system-protobuf=0', '--', '$(gyp_shared_intermediate_dir)/protoc', '--cpp_out', '$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service', '--python_out', '$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service'], 'message': 'Generating C++ and Python code from $(RULE_SOURCES)', 'process_outputs_as_sources': '1'}":
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
-$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
 $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py: $(LOCAL_PATH)/gpu/command_buffer/service/disk_cache_proto.proto $(LOCAL_PATH)/tools/protoc_wrapper/protoc_wrapper.py $(gyp_shared_intermediate_dir)/protoc $(GYP_TARGET_DEPENDENCIES)
 	mkdir -p $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service $(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service; cd $(gyp_local_path)/gpu; python ../tools/protoc_wrapper/protoc_wrapper.py --include "" --protobuf "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h" --proto-in-dir command_buffer/service --proto-in-file "disk_cache_proto$(suffix $<)" "--use-system-protobuf=0" -- "$(gyp_shared_intermediate_dir)/protoc" --cpp_out "$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service" --python_out "$(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service"
 
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
-$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.cc: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
+$(gyp_shared_intermediate_dir)/protoc_out/gpu/command_buffer/service/disk_cache_proto.pb.h: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py ;
 .PHONY: gpu_disk_cache_proto_gyp_rule_trigger
 gpu_disk_cache_proto_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/pyproto/gpu/command_buffer/service/disk_cache_proto_pb2.py
 
@@ -96,6 +96,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -124,9 +125,9 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_c_lib.target.darwin-arm.mk b/gpu/gles2_c_lib.target.darwin-arm.mk
index 321caba..627066b 100644
--- a/gpu/gles2_c_lib.target.darwin-arm.mk
+++ b/gpu/gles2_c_lib.target.darwin-arm.mk
@@ -68,6 +68,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -96,9 +97,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_c_lib.target.darwin-x86.mk b/gpu/gles2_c_lib.target.darwin-x86.mk
index 991a04f..a3dfb83 100644
--- a/gpu/gles2_c_lib.target.darwin-x86.mk
+++ b/gpu/gles2_c_lib.target.darwin-x86.mk
@@ -70,6 +70,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -98,9 +99,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_c_lib.target.linux-arm.mk b/gpu/gles2_c_lib.target.linux-arm.mk
index 321caba..627066b 100644
--- a/gpu/gles2_c_lib.target.linux-arm.mk
+++ b/gpu/gles2_c_lib.target.linux-arm.mk
@@ -68,6 +68,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -96,9 +97,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_c_lib.target.linux-x86.mk b/gpu/gles2_c_lib.target.linux-x86.mk
index 991a04f..a3dfb83 100644
--- a/gpu/gles2_c_lib.target.linux-x86.mk
+++ b/gpu/gles2_c_lib.target.linux-x86.mk
@@ -70,6 +70,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -98,9 +99,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_cmd_helper.target.darwin-arm.mk b/gpu/gles2_cmd_helper.target.darwin-arm.mk
index 166f09a..601bccf 100644
--- a/gpu/gles2_cmd_helper.target.darwin-arm.mk
+++ b/gpu/gles2_cmd_helper.target.darwin-arm.mk
@@ -67,6 +67,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -92,9 +93,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_cmd_helper.target.darwin-x86.mk b/gpu/gles2_cmd_helper.target.darwin-x86.mk
index ebddb4e..b8d6197 100644
--- a/gpu/gles2_cmd_helper.target.darwin-x86.mk
+++ b/gpu/gles2_cmd_helper.target.darwin-x86.mk
@@ -69,6 +69,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -94,9 +95,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_cmd_helper.target.linux-arm.mk b/gpu/gles2_cmd_helper.target.linux-arm.mk
index 166f09a..601bccf 100644
--- a/gpu/gles2_cmd_helper.target.linux-arm.mk
+++ b/gpu/gles2_cmd_helper.target.linux-arm.mk
@@ -67,6 +67,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -92,9 +93,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_cmd_helper.target.linux-x86.mk b/gpu/gles2_cmd_helper.target.linux-x86.mk
index ebddb4e..b8d6197 100644
--- a/gpu/gles2_cmd_helper.target.linux-x86.mk
+++ b/gpu/gles2_cmd_helper.target.linux-x86.mk
@@ -69,6 +69,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -94,9 +95,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_conform_support/egl/display.cc b/gpu/gles2_conform_support/egl/display.cc
index 1d3fea6..6eb6eab 100644
--- a/gpu/gles2_conform_support/egl/display.cc
+++ b/gpu/gles2_conform_support/egl/display.cc
@@ -230,7 +230,8 @@
       NULL,
       transfer_buffer_.get(),
       share_resources,
-      true));
+      true,
+      NULL));
 
   if (!context_->Initialize(
       kTransferBufferSize,
diff --git a/gpu/gles2_conform_support/gles2_conform_support.gyp b/gpu/gles2_conform_support/gles2_conform_support.gyp
index 94ebebf..7e62d32 100644
--- a/gpu/gles2_conform_support/gles2_conform_support.gyp
+++ b/gpu/gles2_conform_support/gles2_conform_support.gyp
@@ -114,6 +114,12 @@
         ['toolkit_uses_gtk == 1', {
           'dependencies': ['../../build/linux/system.gyp:gtk'],
         }],
+        # See http://crbug.com/162998#c4 for why this is needed.
+        ['OS=="linux" and linux_use_tcmalloc==1', {
+          'dependencies': [
+            '../../base/allocator/allocator.gyp:allocator',
+          ],
+        }],
       ],
       'defines': [
         'GLES2_CONFORM_SUPPORT_ONLY',
diff --git a/gpu/gles2_implementation.target.darwin-arm.mk b/gpu/gles2_implementation.target.darwin-arm.mk
index 021ebe8..ba0618d 100644
--- a/gpu/gles2_implementation.target.darwin-arm.mk
+++ b/gpu/gles2_implementation.target.darwin-arm.mk
@@ -30,7 +30,7 @@
 	gpu/command_buffer/client/gles2_implementation.cc \
 	gpu/command_buffer/client/gles2_interface.cc \
 	gpu/command_buffer/client/gles2_trace_implementation.cc \
-	gpu/command_buffer/client/gpu_memory_buffer_factory.cc \
+	gpu/command_buffer/client/gpu_memory_buffer_tracker.cc \
 	gpu/command_buffer/client/program_info_manager.cc \
 	gpu/command_buffer/client/query_tracker.cc \
 	gpu/command_buffer/client/share_group.cc \
@@ -78,6 +78,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -108,9 +109,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_implementation.target.darwin-x86.mk b/gpu/gles2_implementation.target.darwin-x86.mk
index 60a47ef..1d60e5b 100644
--- a/gpu/gles2_implementation.target.darwin-x86.mk
+++ b/gpu/gles2_implementation.target.darwin-x86.mk
@@ -30,7 +30,7 @@
 	gpu/command_buffer/client/gles2_implementation.cc \
 	gpu/command_buffer/client/gles2_interface.cc \
 	gpu/command_buffer/client/gles2_trace_implementation.cc \
-	gpu/command_buffer/client/gpu_memory_buffer_factory.cc \
+	gpu/command_buffer/client/gpu_memory_buffer_tracker.cc \
 	gpu/command_buffer/client/program_info_manager.cc \
 	gpu/command_buffer/client/query_tracker.cc \
 	gpu/command_buffer/client/share_group.cc \
@@ -80,6 +80,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -110,9 +111,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_implementation.target.linux-arm.mk b/gpu/gles2_implementation.target.linux-arm.mk
index 021ebe8..ba0618d 100644
--- a/gpu/gles2_implementation.target.linux-arm.mk
+++ b/gpu/gles2_implementation.target.linux-arm.mk
@@ -30,7 +30,7 @@
 	gpu/command_buffer/client/gles2_implementation.cc \
 	gpu/command_buffer/client/gles2_interface.cc \
 	gpu/command_buffer/client/gles2_trace_implementation.cc \
-	gpu/command_buffer/client/gpu_memory_buffer_factory.cc \
+	gpu/command_buffer/client/gpu_memory_buffer_tracker.cc \
 	gpu/command_buffer/client/program_info_manager.cc \
 	gpu/command_buffer/client/query_tracker.cc \
 	gpu/command_buffer/client/share_group.cc \
@@ -78,6 +78,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -108,9 +109,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gles2_implementation.target.linux-x86.mk b/gpu/gles2_implementation.target.linux-x86.mk
index 60a47ef..1d60e5b 100644
--- a/gpu/gles2_implementation.target.linux-x86.mk
+++ b/gpu/gles2_implementation.target.linux-x86.mk
@@ -30,7 +30,7 @@
 	gpu/command_buffer/client/gles2_implementation.cc \
 	gpu/command_buffer/client/gles2_interface.cc \
 	gpu/command_buffer/client/gles2_trace_implementation.cc \
-	gpu/command_buffer/client/gpu_memory_buffer_factory.cc \
+	gpu/command_buffer/client/gpu_memory_buffer_tracker.cc \
 	gpu/command_buffer/client/program_info_manager.cc \
 	gpu/command_buffer/client/query_tracker.cc \
 	gpu/command_buffer/client/share_group.cc \
@@ -80,6 +80,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -110,9 +111,9 @@
 	$(LOCAL_PATH) \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 0492532..9c72995 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -206,6 +206,21 @@
         'command_buffer/service/transfer_buffer_manager_unittest.cc',
         'command_buffer/service/vertex_attrib_manager_unittest.cc',
         'command_buffer/service/vertex_array_manager_unittest.cc',
+        'config/gpu_blacklist_unittest.cc',
+        'config/gpu_control_list_entry_unittest.cc',
+        'config/gpu_control_list_machine_model_info_unittest.cc',
+        'config/gpu_control_list_number_info_unittest.cc',
+        'config/gpu_control_list_os_info_unittest.cc',
+        'config/gpu_control_list_string_info_unittest.cc',
+        'config/gpu_control_list_unittest.cc',
+        'config/gpu_control_list_version_info_unittest.cc',
+        'config/gpu_driver_bug_list_unittest.cc',
+        'config/gpu_info_collector_unittest.cc',
+        'config/gpu_info_unittest.cc',
+        'config/gpu_switching_list_unittest.cc',
+        'config/gpu_test_config_unittest.cc',
+        'config/gpu_test_expectations_parser_unittest.cc',
+        'config/gpu_util_unittest.cc',
       ],
       'conditions': [
         ['OS == "android" and gtest_target_type == "shared_library"', {
@@ -243,10 +258,15 @@
       ],
       'sources': [
         '<@(gles2_c_lib_source_files)',
+        'command_buffer/client/gpu_memory_buffer_mock.cc',
+        'command_buffer/client/gpu_memory_buffer_mock.h',
+        'command_buffer/client/image_factory_mock.cc',
+        'command_buffer/client/image_factory_mock.h',
         'command_buffer/tests/gl_bind_uniform_location_unittest.cc',
         'command_buffer/tests/gl_chromium_framebuffer_multisample_unittest.cc',
         'command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc',
         'command_buffer/tests/gl_depth_texture_unittest.cc',
+        'command_buffer/tests/gl_gpu_memory_buffer_unittests.cc',
         'command_buffer/tests/gl_lose_context_chromium_unittests.cc',
         'command_buffer/tests/gl_manager.cc',
         'command_buffer/tests/gl_manager.h',
@@ -315,6 +335,7 @@
             'command_buffer_common',
             'command_buffer_service',
             'gles2_cmd_helper',
+            'gpu_config',
             'gpu_ipc',
           ],
           'sources': [
@@ -385,6 +406,13 @@
             'command_buffer_common',
           ],
         },
+        {
+          'target_name': 'gpu_config',
+          'type': 'static_library',
+          'includes': [
+            'gpu_config.gypi',
+          ],
+        },
       ],
     },
     { # component != static_library
@@ -407,6 +435,7 @@
             'command_buffer_common.gypi',
             'command_buffer_service.gypi',
             'gles2_cmd_helper.gypi',
+            'gpu_config.gypi',
             'gpu_ipc.gypi',
           ],
           'defines': [
diff --git a/gpu/gpu.target.darwin-arm.mk b/gpu/gpu.target.darwin-arm.mk
index ab5146c..34be7e1 100644
--- a/gpu/gpu.target.darwin-arm.mk
+++ b/gpu/gpu.target.darwin-arm.mk
@@ -16,6 +16,7 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
@@ -71,6 +72,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -98,9 +100,9 @@
 	$(LOCAL_PATH)/gpu \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu.target.darwin-x86.mk b/gpu/gpu.target.darwin-x86.mk
index 548d95c..e6fa243 100644
--- a/gpu/gpu.target.darwin-x86.mk
+++ b/gpu/gpu.target.darwin-x86.mk
@@ -16,6 +16,7 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
@@ -73,6 +74,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -100,9 +102,9 @@
 	$(LOCAL_PATH)/gpu \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu.target.linux-arm.mk b/gpu/gpu.target.linux-arm.mk
index ab5146c..34be7e1 100644
--- a/gpu/gpu.target.linux-arm.mk
+++ b/gpu/gpu.target.linux-arm.mk
@@ -16,6 +16,7 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
@@ -71,6 +72,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -98,9 +100,9 @@
 	$(LOCAL_PATH)/gpu \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu.target.linux-x86.mk b/gpu/gpu.target.linux-x86.mk
index 548d95c..e6fa243 100644
--- a/gpu/gpu.target.linux-x86.mk
+++ b/gpu/gpu.target.linux-x86.mk
@@ -16,6 +16,7 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
@@ -73,6 +74,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -100,9 +102,9 @@
 	$(LOCAL_PATH)/gpu \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu_common.gypi b/gpu/gpu_common.gypi
index 432457b..14b4921 100644
--- a/gpu/gpu_common.gypi
+++ b/gpu/gpu_common.gypi
@@ -37,8 +37,9 @@
       'command_buffer/client/gles2_trace_implementation.h',
       'command_buffer/client/gles2_trace_implementation_impl_autogen.h',
       'command_buffer/client/gpu_memory_buffer.h',
-      'command_buffer/client/gpu_memory_buffer_factory.cc',
-      'command_buffer/client/gpu_memory_buffer_factory.h',
+      'command_buffer/client/gpu_memory_buffer_tracker.h',
+      'command_buffer/client/gpu_memory_buffer_tracker.cc',
+      'command_buffer/client/image_factory.h',
       'command_buffer/client/program_info_manager.cc',
       'command_buffer/client/program_info_manager.h',
       'command_buffer/client/query_tracker.cc',
diff --git a/gpu/gpu_config.gypi b/gpu/gpu_config.gypi
new file mode 100644
index 0000000..c95605b
--- /dev/null
+++ b/gpu/gpu_config.gypi
@@ -0,0 +1,77 @@
+# Copyright (c) 2012 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.
+
+{
+  'dependencies': [
+    '../base/base.gyp:base',
+    '../third_party/re2/re2.gyp:re2',
+    '../ui/gl/gl.gyp:gl',
+  ],
+  'include_dirs': [
+    '..',
+  ],
+  'sources': [
+    'config/dx_diag_node.cc',
+    'config/dx_diag_node.h',
+    'config/gpu_blacklist.cc',
+    'config/gpu_blacklist.h',
+    'config/gpu_control_list_jsons.h',
+    'config/gpu_control_list.cc',
+    'config/gpu_control_list.h',
+    'config/gpu_driver_bug_list_json.cc',
+    'config/gpu_driver_bug_list.cc',
+    'config/gpu_driver_bug_list.h',
+    'config/gpu_driver_bug_workaround_type.h',
+    'config/gpu_dx_diagnostics_win.cc',
+    'config/gpu_feature_type.h',
+    'config/gpu_info.cc',
+    'config/gpu_info.h',
+    'config/gpu_info_collector_android.cc',
+    'config/gpu_info_collector_mac.mm',
+    'config/gpu_info_collector_ozone.cc',
+    'config/gpu_info_collector_win.cc',
+    'config/gpu_info_collector_x11.cc',
+    'config/gpu_info_collector.cc',
+    'config/gpu_info_collector.h',
+    'config/gpu_performance_stats.h',
+    'config/gpu_switching_list_json.cc',
+    'config/gpu_switching_list.cc',
+    'config/gpu_switching_list.h',
+    'config/gpu_switching_option.h',
+    'config/gpu_test_config.cc',
+    'config/gpu_test_config.h',
+    'config/gpu_test_expectations_parser.cc',
+    'config/gpu_test_expectations_parser.h',
+    'config/gpu_util.cc',
+    'config/gpu_util.h',
+    'config/software_rendering_list_json.cc',
+  ],
+  'conditions': [
+    ['OS=="win"', {
+      'dependencies': [
+        '../third_party/libxml/libxml.gyp:libxml',
+      ],
+      'link_settings': {
+        'libraries': [
+          '-ldxguid.lib',
+          '-lsetupapi.lib',
+        ],
+      },
+      # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+      'msvs_disabled_warnings': [ 4267, ],
+    }],
+    ['OS=="win" and branding=="Chrome"', {
+      'sources': [
+        '../third_party/amd/AmdCfxPxExt.h',
+        '../third_party/amd/amd_videocard_info_win.cc',
+      ],
+    }],
+    ['OS=="linux" and use_x11==1', {
+      'dependencies': [
+        '../build/linux/system.gyp:libpci',
+        '../third_party/libXNVCtrl/libXNVCtrl.gyp:libXNVCtrl',
+      ],
+    }],
+  ],
+}
diff --git a/gpu/gpu_config.target.darwin-arm.mk b/gpu/gpu_config.target.darwin-arm.mk
new file mode 100644
index 0000000..9208820
--- /dev/null
+++ b/gpu/gpu_config.target.darwin-arm.mk
@@ -0,0 +1,169 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := gpu_gpu_config_gyp
+LOCAL_MODULE_SUFFIX := .a
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a
+
+GYP_GENERATED_OUTPUTS :=
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES := \
+	gpu/config/dx_diag_node.cc \
+	gpu/config/gpu_blacklist.cc \
+	gpu/config/gpu_control_list.cc \
+	gpu/config/gpu_driver_bug_list_json.cc \
+	gpu/config/gpu_driver_bug_list.cc \
+	gpu/config/gpu_info.cc \
+	gpu/config/gpu_info_collector_android.cc \
+	gpu/config/gpu_info_collector.cc \
+	gpu/config/gpu_switching_list_json.cc \
+	gpu/config/gpu_switching_list.cc \
+	gpu/config/gpu_test_config.cc \
+	gpu/config/gpu_test_expectations_parser.cc \
+	gpu/config/gpu_util.cc \
+	gpu/config/software_rendering_list_json.cc
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-Werror \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-fno-tree-sra \
+	-fuse-ld=gold \
+	-Wno-psabi \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fstack-protector \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-D__STDC_CONSTANT_MACROS' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH) \
+	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH)/third_party/khronos \
+	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
+	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
+	$(LOCAL_PATH)/third_party/re2 \
+	$(gyp_shared_intermediate_dir)/ui/gl \
+	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wsign-compare \
+	-Wno-abi \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo
+
+### Rules for final target.
+
+LOCAL_LDFLAGS := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-Wl,-z,relro \
+	-Wl,-z,now \
+	-fuse-ld=gold \
+	-nostdlib \
+	-Wl,--no-undefined \
+	-Wl,--exclude-libs=ALL \
+	-Wl,--icf=safe \
+	-Wl,--gc-sections \
+	-Wl,-O1 \
+	-Wl,--as-needed
+
+
+LOCAL_STATIC_LIBRARIES := \
+	ui_gl_gl_gyp
+
+# Enable grouping to fix circular references
+LOCAL_GROUP_STATIC_LIBRARIES := true
+
+LOCAL_SHARED_LIBRARIES := \
+	libstlport \
+	libdl
+
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: gpu_gpu_config_gyp
+
+# Alias gyp target name.
+.PHONY: gpu_config
+gpu_config: gpu_gpu_config_gyp
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/gpu/gpu_config.target.darwin-x86.mk b/gpu/gpu_config.target.darwin-x86.mk
new file mode 100644
index 0000000..1e7efb8
--- /dev/null
+++ b/gpu/gpu_config.target.darwin-x86.mk
@@ -0,0 +1,168 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := gpu_gpu_config_gyp
+LOCAL_MODULE_SUFFIX := .a
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a
+
+GYP_GENERATED_OUTPUTS :=
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES := \
+	gpu/config/dx_diag_node.cc \
+	gpu/config/gpu_blacklist.cc \
+	gpu/config/gpu_control_list.cc \
+	gpu/config/gpu_driver_bug_list_json.cc \
+	gpu/config/gpu_driver_bug_list.cc \
+	gpu/config/gpu_info.cc \
+	gpu/config/gpu_info_collector_android.cc \
+	gpu/config/gpu_info_collector.cc \
+	gpu/config/gpu_switching_list_json.cc \
+	gpu/config/gpu_switching_list.cc \
+	gpu/config/gpu_test_config.cc \
+	gpu/config/gpu_test_expectations_parser.cc \
+	gpu/config/gpu_util.cc \
+	gpu/config/software_rendering_list_json.cc
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	--param=ssp-buffer-size=4 \
+	-Werror \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-m32 \
+	-mmmx \
+	-march=pentium4 \
+	-msse2 \
+	-mfpmath=sse \
+	-fuse-ld=gold \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-fno-stack-protector \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-D__STDC_CONSTANT_MACROS' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH) \
+	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH)/third_party/khronos \
+	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
+	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
+	$(LOCAL_PATH)/third_party/re2 \
+	$(gyp_shared_intermediate_dir)/ui/gl \
+	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wsign-compare \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo
+
+### Rules for final target.
+
+LOCAL_LDFLAGS := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-m32 \
+	-fuse-ld=gold \
+	-nostdlib \
+	-Wl,--no-undefined \
+	-Wl,--exclude-libs=ALL \
+	-Wl,--gc-sections \
+	-Wl,-O1 \
+	-Wl,--as-needed
+
+
+LOCAL_STATIC_LIBRARIES := \
+	ui_gl_gl_gyp
+
+# Enable grouping to fix circular references
+LOCAL_GROUP_STATIC_LIBRARIES := true
+
+LOCAL_SHARED_LIBRARIES := \
+	libstlport \
+	libdl
+
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: gpu_gpu_config_gyp
+
+# Alias gyp target name.
+.PHONY: gpu_config
+gpu_config: gpu_gpu_config_gyp
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/gpu/gpu_config.target.linux-arm.mk b/gpu/gpu_config.target.linux-arm.mk
new file mode 100644
index 0000000..9208820
--- /dev/null
+++ b/gpu/gpu_config.target.linux-arm.mk
@@ -0,0 +1,169 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := gpu_gpu_config_gyp
+LOCAL_MODULE_SUFFIX := .a
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a
+
+GYP_GENERATED_OUTPUTS :=
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES := \
+	gpu/config/dx_diag_node.cc \
+	gpu/config/gpu_blacklist.cc \
+	gpu/config/gpu_control_list.cc \
+	gpu/config/gpu_driver_bug_list_json.cc \
+	gpu/config/gpu_driver_bug_list.cc \
+	gpu/config/gpu_info.cc \
+	gpu/config/gpu_info_collector_android.cc \
+	gpu/config/gpu_info_collector.cc \
+	gpu/config/gpu_switching_list_json.cc \
+	gpu/config/gpu_switching_list.cc \
+	gpu/config/gpu_test_config.cc \
+	gpu/config/gpu_test_expectations_parser.cc \
+	gpu/config/gpu_util.cc \
+	gpu/config/software_rendering_list_json.cc
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-Werror \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-fno-tree-sra \
+	-fuse-ld=gold \
+	-Wno-psabi \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fstack-protector \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-D__STDC_CONSTANT_MACROS' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH) \
+	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH)/third_party/khronos \
+	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
+	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
+	$(LOCAL_PATH)/third_party/re2 \
+	$(gyp_shared_intermediate_dir)/ui/gl \
+	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wsign-compare \
+	-Wno-abi \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo
+
+### Rules for final target.
+
+LOCAL_LDFLAGS := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-Wl,-z,relro \
+	-Wl,-z,now \
+	-fuse-ld=gold \
+	-nostdlib \
+	-Wl,--no-undefined \
+	-Wl,--exclude-libs=ALL \
+	-Wl,--icf=safe \
+	-Wl,--gc-sections \
+	-Wl,-O1 \
+	-Wl,--as-needed
+
+
+LOCAL_STATIC_LIBRARIES := \
+	ui_gl_gl_gyp
+
+# Enable grouping to fix circular references
+LOCAL_GROUP_STATIC_LIBRARIES := true
+
+LOCAL_SHARED_LIBRARIES := \
+	libstlport \
+	libdl
+
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: gpu_gpu_config_gyp
+
+# Alias gyp target name.
+.PHONY: gpu_config
+gpu_config: gpu_gpu_config_gyp
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/gpu/gpu_config.target.linux-x86.mk b/gpu/gpu_config.target.linux-x86.mk
new file mode 100644
index 0000000..1e7efb8
--- /dev/null
+++ b/gpu/gpu_config.target.linux-x86.mk
@@ -0,0 +1,168 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := gpu_gpu_config_gyp
+LOCAL_MODULE_SUFFIX := .a
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a
+
+GYP_GENERATED_OUTPUTS :=
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES := \
+	gpu/config/dx_diag_node.cc \
+	gpu/config/gpu_blacklist.cc \
+	gpu/config/gpu_control_list.cc \
+	gpu/config/gpu_driver_bug_list_json.cc \
+	gpu/config/gpu_driver_bug_list.cc \
+	gpu/config/gpu_info.cc \
+	gpu/config/gpu_info_collector_android.cc \
+	gpu/config/gpu_info_collector.cc \
+	gpu/config/gpu_switching_list_json.cc \
+	gpu/config/gpu_switching_list.cc \
+	gpu/config/gpu_test_config.cc \
+	gpu/config/gpu_test_expectations_parser.cc \
+	gpu/config/gpu_util.cc \
+	gpu/config/software_rendering_list_json.cc
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	--param=ssp-buffer-size=4 \
+	-Werror \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-m32 \
+	-mmmx \
+	-march=pentium4 \
+	-msse2 \
+	-mfpmath=sse \
+	-fuse-ld=gold \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-fno-stack-protector \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-D__STDC_CONSTANT_MACROS' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH) \
+	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH)/third_party/khronos \
+	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
+	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
+	$(LOCAL_PATH)/third_party/re2 \
+	$(gyp_shared_intermediate_dir)/ui/gl \
+	$(LOCAL_PATH)/third_party/mesa/MesaLib/include \
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wsign-compare \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo
+
+### Rules for final target.
+
+LOCAL_LDFLAGS := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-m32 \
+	-fuse-ld=gold \
+	-nostdlib \
+	-Wl,--no-undefined \
+	-Wl,--exclude-libs=ALL \
+	-Wl,--gc-sections \
+	-Wl,-O1 \
+	-Wl,--as-needed
+
+
+LOCAL_STATIC_LIBRARIES := \
+	ui_gl_gl_gyp
+
+# Enable grouping to fix circular references
+LOCAL_GROUP_STATIC_LIBRARIES := true
+
+LOCAL_SHARED_LIBRARIES := \
+	libstlport \
+	libdl
+
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: gpu_gpu_config_gyp
+
+# Alias gyp target name.
+.PHONY: gpu_config
+gpu_config: gpu_gpu_config_gyp
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/gpu/gpu_ipc.target.darwin-arm.mk b/gpu/gpu_ipc.target.darwin-arm.mk
index 61e5ca7..13214e8 100644
--- a/gpu/gpu_ipc.target.darwin-arm.mk
+++ b/gpu/gpu_ipc.target.darwin-arm.mk
@@ -69,6 +69,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -94,9 +95,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu_ipc.target.darwin-x86.mk b/gpu/gpu_ipc.target.darwin-x86.mk
index 58909c8..836b642 100644
--- a/gpu/gpu_ipc.target.darwin-x86.mk
+++ b/gpu/gpu_ipc.target.darwin-x86.mk
@@ -71,6 +71,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -96,9 +97,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu_ipc.target.linux-arm.mk b/gpu/gpu_ipc.target.linux-arm.mk
index 61e5ca7..13214e8 100644
--- a/gpu/gpu_ipc.target.linux-arm.mk
+++ b/gpu/gpu_ipc.target.linux-arm.mk
@@ -69,6 +69,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -94,9 +95,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
 
diff --git a/gpu/gpu_ipc.target.linux-x86.mk b/gpu/gpu_ipc.target.linux-x86.mk
index 58909c8..836b642 100644
--- a/gpu/gpu_ipc.target.linux-x86.mk
+++ b/gpu/gpu_ipc.target.linux-x86.mk
@@ -71,6 +71,7 @@
 	'-DNO_TCMALLOC' \
 	'-DDISABLE_NACL' \
 	'-DCHROMIUM_BUILD' \
+	'-DENABLE_DOUBLE_RESOURCE_LOAD_TIMING' \
 	'-DUSE_LIBJPEG_TURBO=1' \
 	'-DUSE_PROPRIETARY_CODECS' \
 	'-DENABLE_GPU=1' \
@@ -96,9 +97,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
-	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
-	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+	$(PWD)/frameworks/wilhelm/include \
+	$(PWD)/bionic \
+	$(PWD)/external/stlport/stlport
 
 LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)