| Index: gpu/command_buffer/client/gles2_implementation.cc | 
| diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc | 
| index 241056cefd013454dd6b64f840c6bdcea1eff4a2..9e7ff75559a6493bd18f64aab386a76bba80c9bd 100644 | 
| --- a/gpu/command_buffer/client/gles2_implementation.cc | 
| +++ b/gpu/command_buffer/client/gles2_implementation.cc | 
| @@ -1323,8 +1323,12 @@ void GLES2Implementation::BufferDataHelper( | 
| if (buffer) | 
| RemoveTransferBuffer(buffer); | 
|  | 
| -    // Create new buffer. | 
| -    buffer = buffer_tracker_->CreateBuffer(buffer_id, size); | 
| +    // Create new buffer.  For the async readback use case, allocate a few | 
| +    // extra bytes to contain any service-side glMapBuffer errors. | 
| +    const GLsizeiptr metadata_size = | 
| +        target == GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM ? | 
| +            sizeof(cmds::ReadPixels::Result) : 0; | 
| +    buffer = buffer_tracker_->CreateBuffer(buffer_id, size, metadata_size); | 
| DCHECK(buffer); | 
| if (buffer->address() && data) | 
| memcpy(buffer->address(), data, size); | 
| @@ -2249,17 +2253,23 @@ void GLES2Implementation::ReadPixels( | 
| return; | 
| } | 
|  | 
| -  if (bound_pixel_pack_transfer_buffer_id_) { | 
| +  if (bound_pixel_pack_transfer_buffer_id_) {  // Asynchronous readback path. | 
| GLuint offset = ToGLuint(pixels); | 
| BufferTracker::Buffer* buffer = GetBoundPixelUnpackTransferBufferIfValid( | 
| bound_pixel_pack_transfer_buffer_id_, | 
| "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(), | 
| -                          0, 0, true); | 
| -      CheckGLError(); | 
| +    if (!buffer || buffer->shm_id() == -1) { | 
| +      SetGLError(GL_INVALID_OPERATION, "glReadPixels", "invalid buffer."); | 
| +      return; | 
| } | 
| + | 
| +    DCHECK_EQ(buffer->metadata_size(), sizeof(Result)); | 
| +    helper_->ReadPixels( | 
| +        xoffset, yoffset, width, height, format, type, | 
| +        buffer->shm_id(), buffer->shm_offset(), | 
| +        buffer->metadata_shm_id(), buffer->metadata_shm_offset(), | 
| +        true); | 
| +    CheckGLError(); | 
| return; | 
| } | 
|  | 
| @@ -2285,38 +2295,37 @@ void GLES2Implementation::ReadPixels( | 
| if (!result) { | 
| return; | 
| } | 
| -    *result = 0;  // mark as failed. | 
| helper_->ReadPixels( | 
| xoffset, yoffset, width, num_rows, format, type, | 
| buffer.shm_id(), buffer.offset(), | 
| GetResultShmId(), GetResultShmOffset(), | 
| false); | 
| WaitForCmd(); | 
| -    if (*result != 0) { | 
| -      // when doing a y-flip we have to iterate through top-to-bottom chunks | 
| -      // of the dst. The service side handles reversing the rows within a | 
| -      // chunk. | 
| -      int8* rows_dst; | 
| -      if (pack_reverse_row_order_) { | 
| -          rows_dst = dest + (height - num_rows) * padded_row_size; | 
| -      } else { | 
| -          rows_dst = dest; | 
| -      } | 
| -      // We have to copy 1 row at a time to avoid writing pad bytes. | 
| -      const int8* src = static_cast<const int8*>(buffer.address()); | 
| -      for (GLint yy = 0; yy < num_rows; ++yy) { | 
| -        memcpy(rows_dst, src, unpadded_row_size); | 
| -        rows_dst += padded_row_size; | 
| -        src += padded_row_size; | 
| -      } | 
| -      if (!pack_reverse_row_order_) { | 
| -        dest = rows_dst; | 
| -      } | 
| -    } | 
| -    // If it was not marked as successful exit. | 
| -    if (*result == 0) { | 
| +    if (*result != GL_NO_ERROR) { | 
| +      SetGLError(*result, "glReadPixels", "Service-side error."); | 
| return; | 
| } | 
| + | 
| +    // when doing a y-flip we have to iterate through top-to-bottom chunks | 
| +    // of the dst. The service side handles reversing the rows within a | 
| +    // chunk. | 
| +    int8* rows_dst; | 
| +    if (pack_reverse_row_order_) { | 
| +        rows_dst = dest + (height - num_rows) * padded_row_size; | 
| +    } else { | 
| +        rows_dst = dest; | 
| +    } | 
| +    // We have to copy 1 row at a time to avoid writing pad bytes. | 
| +    const int8* src = static_cast<const int8*>(buffer.address()); | 
| +    for (GLint yy = 0; yy < num_rows; ++yy) { | 
| +      memcpy(rows_dst, src, unpadded_row_size); | 
| +      rows_dst += padded_row_size; | 
| +      src += padded_row_size; | 
| +    } | 
| +    if (!pack_reverse_row_order_) { | 
| +      dest = rows_dst; | 
| +    } | 
| + | 
| yoffset += num_rows; | 
| height -= num_rows; | 
| } | 
| @@ -3676,6 +3685,7 @@ void* GLES2Implementation::MapBufferCHROMIUM(GLuint target, GLenum access) { | 
| GLuint buffer_id; | 
| GetBoundPixelTransferBuffer(target, "glMapBufferCHROMIUM", &buffer_id); | 
| if (!buffer_id) { | 
| +    SetGLError(GL_INVALID_VALUE, "glMapBufferCHROMIUM", "invalid buffer"); | 
| return NULL; | 
| } | 
| BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id); | 
| @@ -3683,6 +3693,17 @@ void* GLES2Implementation::MapBufferCHROMIUM(GLuint target, GLenum access) { | 
| SetGLError(GL_INVALID_OPERATION, "glMapBufferCHROMIUM", "invalid buffer"); | 
| return NULL; | 
| } | 
| + | 
| +  if (target == GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM) { | 
| +    DCHECK_EQ(buffer->metadata_size(), sizeof(cmds::ReadPixels::Result)); | 
| +    const GLenum error = | 
| +        *(static_cast<cmds::ReadPixels::Result*>(buffer->metadata_address())); | 
| +    if (error != GL_NO_ERROR) { | 
| +      SetGLError(error, "glMapBufferCHROMIUM", "service map buffer error"); | 
| +      return NULL; | 
| +    } | 
| +  } | 
| + | 
| if (buffer->mapped()) { | 
| SetGLError(GL_INVALID_OPERATION, "glMapBufferCHROMIUM", "already mapped"); | 
| return NULL; | 
|  |