| Index: gpu/command_buffer/service/gles2_cmd_decoder.cc
 | 
| ===================================================================
 | 
| --- gpu/command_buffer/service/gles2_cmd_decoder.cc	(revision 93137)
 | 
| +++ gpu/command_buffer/service/gles2_cmd_decoder.cc	(working copy)
 | 
| @@ -479,6 +479,7 @@
 | 
|  
 | 
|    virtual void SetResizeCallback(Callback1<gfx::Size>::Type* callback);
 | 
|    virtual void SetSwapBuffersCallback(Callback0::Type* callback);
 | 
| +  virtual void SetLatchCallback(const base::Callback<void(bool)>& callback);;
 | 
|    virtual bool GetServiceTextureId(uint32 client_texture_id,
 | 
|                                     uint32* service_texture_id);
 | 
|  
 | 
| @@ -1271,6 +1272,7 @@
 | 
|  
 | 
|    scoped_ptr<Callback1<gfx::Size>::Type> resize_callback_;
 | 
|    scoped_ptr<Callback0::Type> swap_buffers_callback_;
 | 
| +  base::Callback<void(bool)> latch_callback_;
 | 
|  
 | 
|    // The format of the back buffer_
 | 
|    GLenum back_buffer_color_format_;
 | 
| @@ -2358,6 +2360,11 @@
 | 
|    swap_buffers_callback_.reset(callback);
 | 
|  }
 | 
|  
 | 
| +void GLES2DecoderImpl::SetLatchCallback(
 | 
| +    const base::Callback<void(bool)>& callback) {
 | 
| +  latch_callback_ = callback;
 | 
| +}
 | 
| +
 | 
|  bool GLES2DecoderImpl::GetServiceTextureId(uint32 client_texture_id,
 | 
|                                             uint32* service_texture_id) {
 | 
|    TextureManager::TextureInfo* texture =
 | 
| @@ -6529,6 +6536,62 @@
 | 
|    return error::kNoError;
 | 
|  }
 | 
|  
 | 
| +error::Error GLES2DecoderImpl::HandleSetLatchCHROMIUM(
 | 
| +    uint32 immediate_data_size, const gles2::SetLatchCHROMIUM& c) {
 | 
| +  TRACE_EVENT1("gpu", "SetLatch", "latch_id", c.latch_id);
 | 
| +  // Ensure the side effects of previous commands are visible to other contexts.
 | 
| +  // There is no need to do this for ANGLE because it uses a
 | 
| +  // single D3D device for all contexts.
 | 
| +  if (!IsAngle())
 | 
| +    glFlush();
 | 
| +
 | 
| +  int32 shm_id = gpu::kLatchSharedMemoryId;
 | 
| +  uint32 latch_id = c.latch_id;
 | 
| +  uint32 shm_offset = 0;
 | 
| +  base::subtle::Atomic32* latch;
 | 
| +  if (!SafeMultiplyUint32(latch_id, sizeof(*latch), &shm_offset)) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +  latch = GetSharedMemoryAs<base::subtle::Atomic32*>(
 | 
| +      shm_id, shm_offset, sizeof(*latch));
 | 
| +  if (!latch) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +  base::subtle::Atomic32 old =
 | 
| +      base::subtle::NoBarrier_CompareAndSwap(latch, 0, 1);
 | 
| +  DCHECK(old == 0);
 | 
| +  if (!latch_callback_.is_null())
 | 
| +    latch_callback_.Run(true);
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
| +error::Error GLES2DecoderImpl::HandleWaitLatchCHROMIUM(
 | 
| +    uint32 immediate_data_size, const gles2::WaitLatchCHROMIUM& c) {
 | 
| +  TRACE_EVENT1("gpu", "WaitLatch", "latch_id", c.latch_id);
 | 
| +  int32 shm_id = gpu::kLatchSharedMemoryId;
 | 
| +  uint32 latch_id = c.latch_id;
 | 
| +  uint32 shm_offset = 0;
 | 
| +  base::subtle::Atomic32* latch;
 | 
| +  if (!SafeMultiplyUint32(latch_id, sizeof(*latch), &shm_offset)) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +  latch = GetSharedMemoryAs<base::subtle::Atomic32*>(
 | 
| +      shm_id, shm_offset, sizeof(*latch));
 | 
| +  if (!latch) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +
 | 
| +  base::subtle::Atomic32 old =
 | 
| +      base::subtle::NoBarrier_CompareAndSwap(latch, 1, 0);
 | 
| +  if (old == 0) {
 | 
| +    if (!latch_callback_.is_null())
 | 
| +      latch_callback_.Run(false);
 | 
| +    return error::kWaiting;
 | 
| +  } else {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  error::Error GLES2DecoderImpl::HandleCommandBufferEnableCHROMIUM(
 | 
|      uint32 immediate_data_size, const gles2::CommandBufferEnableCHROMIUM& c) {
 | 
|    Bucket* bucket = GetBucket(c.bucket_id);
 | 
| 
 |