| Index: gpu/command_buffer/service/gles2_cmd_decoder.cc
 | 
| diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
 | 
| index 9b8e63521ebd82282316474833e1feed8031452a..52a9c4351b47185261d76eba400f20aec9bb0b41 100644
 | 
| --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
 | 
| +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
 | 
| @@ -1684,6 +1684,11 @@ class GLES2DecoderImpl : public GLES2Decoder,
 | 
|             surface_->DeferDraws();
 | 
|    }
 | 
|  
 | 
| +  bool IsRobustnessSupported() {
 | 
| +    return has_robustness_extension_ &&
 | 
| +           context_->WasAllocatedUsingRobustnessExtension();
 | 
| +  }
 | 
| +
 | 
|    error::Error WillAccessBoundFramebufferForDraw() {
 | 
|      if (ShouldDeferDraws())
 | 
|        return error::kDeferCommandUntilLater;
 | 
| @@ -1820,8 +1825,8 @@ class GLES2DecoderImpl : public GLES2Decoder,
 | 
|    error::Error current_decoder_error_;
 | 
|  
 | 
|    bool use_shader_translator_;
 | 
| -  scoped_refptr<ShaderTranslator> vertex_translator_;
 | 
| -  scoped_refptr<ShaderTranslator> fragment_translator_;
 | 
| +  scoped_refptr<ShaderTranslatorInterface> vertex_translator_;
 | 
| +  scoped_refptr<ShaderTranslatorInterface> fragment_translator_;
 | 
|  
 | 
|    DisallowedFeatures disallowed_features_;
 | 
|  
 | 
| @@ -5837,15 +5842,7 @@ void GLES2DecoderImpl::DoLinkProgram(GLuint program_id) {
 | 
|    }
 | 
|  
 | 
|    LogClientServiceForInfo(program, program_id, "glLinkProgram");
 | 
| -  ShaderTranslator* vertex_translator = NULL;
 | 
| -  ShaderTranslator* fragment_translator = NULL;
 | 
| -  if (use_shader_translator_) {
 | 
| -    vertex_translator = vertex_translator_.get();
 | 
| -    fragment_translator = fragment_translator_.get();
 | 
| -  }
 | 
|    if (program->Link(shader_manager(),
 | 
| -                    vertex_translator,
 | 
| -                    fragment_translator,
 | 
|                      workarounds().count_all_in_varyings_packing ?
 | 
|                          Program::kCountAll : Program::kCountOnlyStaticallyUsed,
 | 
|                      shader_cache_callback_)) {
 | 
| @@ -7091,24 +7088,17 @@ void GLES2DecoderImpl::DoCompileShader(GLuint client_id) {
 | 
|    if (!shader) {
 | 
|      return;
 | 
|    }
 | 
| -  ShaderTranslator* translator = NULL;
 | 
| +
 | 
| +  scoped_refptr<ShaderTranslatorInterface> translator;
 | 
|    if (use_shader_translator_) {
 | 
| -    translator = shader->shader_type() == GL_VERTEX_SHADER ?
 | 
| -        vertex_translator_.get() : fragment_translator_.get();
 | 
| +      translator = shader->shader_type() == GL_VERTEX_SHADER ?
 | 
| +                   vertex_translator_ : fragment_translator_;
 | 
|    }
 | 
|  
 | 
| -  shader->RequestCompile();
 | 
| -
 | 
| -  // TODO(dyen): Currently we compile immediately when glCompileShader is
 | 
| -  // requested. Eventually this should be deffered to the linker stage.
 | 
| -  shader->DoCompile(
 | 
| -     translator,
 | 
| -     feature_info_->feature_flags().angle_translated_shader_source ?
 | 
| -         Shader::kANGLE : Shader::kGL);
 | 
| -
 | 
| -  // CompileShader can be very slow.  Exit command processing to allow for
 | 
| -  // context preemption and GPU watchdog checks.
 | 
| -  ExitCommandProcessingEarly();
 | 
| +  const Shader::TranslatedShaderSourceType source_type =
 | 
| +      feature_info_->feature_flags().angle_translated_shader_source ?
 | 
| +      Shader::kANGLE : Shader::kGL;
 | 
| +  shader->RequestCompile(translator, source_type);
 | 
|  }
 | 
|  
 | 
|  void GLES2DecoderImpl::DoGetShaderiv(
 | 
| @@ -7117,6 +7107,19 @@ void GLES2DecoderImpl::DoGetShaderiv(
 | 
|    if (!shader) {
 | 
|      return;
 | 
|    }
 | 
| +
 | 
| +  // Compile now for statuses that require it.
 | 
| +  switch (pname) {
 | 
| +    case GL_COMPILE_STATUS:
 | 
| +    case GL_INFO_LOG_LENGTH:
 | 
| +    case GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE:
 | 
| +      shader->DoCompile();
 | 
| +      break;
 | 
| +
 | 
| +    default:
 | 
| +    break;
 | 
| +  }
 | 
| +
 | 
|    switch (pname) {
 | 
|      case GL_SHADER_SOURCE_LENGTH:
 | 
|        *params = shader->source().size();
 | 
| @@ -7174,6 +7177,9 @@ error::Error GLES2DecoderImpl::HandleGetTranslatedShaderSourceANGLE(
 | 
|      return error::kNoError;
 | 
|    }
 | 
|  
 | 
| +  // Make sure translator has been utilized in compile.
 | 
| +  shader->DoCompile();
 | 
| +
 | 
|    bucket->SetFromString(shader->translated_source().c_str());
 | 
|    return error::kNoError;
 | 
|  }
 | 
| @@ -7209,6 +7215,10 @@ error::Error GLES2DecoderImpl::HandleGetShaderInfoLog(
 | 
|      bucket->SetFromString("");
 | 
|      return error::kNoError;
 | 
|    }
 | 
| +
 | 
| +  // Shader must be compiled in order to get the info log.
 | 
| +  shader->DoCompile();
 | 
| +
 | 
|    bucket->SetFromString(shader->log_info().c_str());
 | 
|    return error::kNoError;
 | 
|  }
 | 
| @@ -8160,6 +8170,58 @@ error::Error GLES2DecoderImpl::HandleGetUniformLocation(
 | 
|      c.program, c.location_shm_id, c.location_shm_offset, name_str);
 | 
|  }
 | 
|  
 | 
| +error::Error GLES2DecoderImpl::HandleGetUniformIndices(
 | 
| +    uint32 immediate_data_size,
 | 
| +    const void* cmd_data) {
 | 
| +  if (!unsafe_es3_apis_enabled())
 | 
| +    return error::kUnknownCommand;
 | 
| +  const gles2::cmds::GetUniformIndices& c =
 | 
| +      *static_cast<const gles2::cmds::GetUniformIndices*>(cmd_data);
 | 
| +  Bucket* bucket = GetBucket(c.names_bucket_id);
 | 
| +  if (!bucket) {
 | 
| +    return error::kInvalidArguments;
 | 
| +  }
 | 
| +  GLsizei count = 0;
 | 
| +  std::vector<char*> names;
 | 
| +  std::vector<GLint> len;
 | 
| +  if (!bucket->GetAsStrings(&count, &names, &len) || count <= 0) {
 | 
| +    return error::kInvalidArguments;
 | 
| +  }
 | 
| +  typedef cmds::GetUniformIndices::Result Result;
 | 
| +  Result* result = GetSharedMemoryAs<Result*>(
 | 
| +      c.indices_shm_id, c.indices_shm_offset,
 | 
| +      Result::ComputeSize(static_cast<size_t>(count)));
 | 
| +  GLuint* indices = result ? result->GetData() : NULL;
 | 
| +  if (indices == NULL) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +  // Check that the client initialized the result.
 | 
| +  if (result->size != 0) {
 | 
| +    return error::kInvalidArguments;
 | 
| +  }
 | 
| +  Program* program = GetProgramInfoNotShader(c.program, "glGetUniformIndices");
 | 
| +  if (!program) {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  GLuint service_id = program->service_id();
 | 
| +  GLint link_status = GL_FALSE;
 | 
| +  glGetProgramiv(service_id, GL_LINK_STATUS, &link_status);
 | 
| +  if (link_status != GL_TRUE) {
 | 
| +    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
 | 
| +        "glGetUniformIndices", "program not linked");
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetUniformIndices");
 | 
| +  glGetUniformIndices(service_id, count, &names[0], indices);
 | 
| +  GLenum error = glGetError();
 | 
| +  if (error == GL_NO_ERROR) {
 | 
| +    result->SetNumResults(count);
 | 
| +  } else {
 | 
| +    LOCAL_SET_GL_ERROR(error, "GetUniformIndices", "");
 | 
| +  }
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
|  error::Error GLES2DecoderImpl::GetFragDataLocationHelper(
 | 
|      GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
 | 
|      const std::string& name_str) {
 | 
| @@ -9730,6 +9792,63 @@ error::Error GLES2DecoderImpl::HandleGetActiveUniform(
 | 
|    return error::kNoError;
 | 
|  }
 | 
|  
 | 
| +error::Error GLES2DecoderImpl::HandleGetActiveUniformBlockiv(
 | 
| +    uint32 immediate_data_size, const void* cmd_data) {
 | 
| +  if (!unsafe_es3_apis_enabled())
 | 
| +    return error::kUnknownCommand;
 | 
| +  const gles2::cmds::GetActiveUniformBlockiv& c =
 | 
| +      *static_cast<const gles2::cmds::GetActiveUniformBlockiv*>(cmd_data);
 | 
| +  GLuint program_id = c.program;
 | 
| +  GLuint index = static_cast<GLuint>(c.index);
 | 
| +  GLenum pname = static_cast<GLenum>(c.pname);
 | 
| +  Program* program = GetProgramInfoNotShader(
 | 
| +      program_id, "glGetActiveUniformBlockiv");
 | 
| +  if (!program) {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  GLuint service_id = program->service_id();
 | 
| +  GLint link_status = GL_FALSE;
 | 
| +  glGetProgramiv(service_id, GL_LINK_STATUS, &link_status);
 | 
| +  if (link_status != GL_TRUE) {
 | 
| +    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
 | 
| +        "glGetActiveActiveUniformBlockiv", "program not linked");
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetActiveUniformBlockiv");
 | 
| +  GLsizei num_values = 1;
 | 
| +  if (pname == GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES) {
 | 
| +    GLint num = 0;
 | 
| +    glGetActiveUniformBlockiv(
 | 
| +        service_id, index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &num);
 | 
| +    GLenum error = glGetError();
 | 
| +    if (error != GL_NO_ERROR) {
 | 
| +      // Assume this will the same error if calling with pname.
 | 
| +      LOCAL_SET_GL_ERROR(error, "GetActiveUniformBlockiv", "");
 | 
| +      return error::kNoError;
 | 
| +    }
 | 
| +    num_values = static_cast<GLsizei>(num);
 | 
| +  }
 | 
| +  typedef cmds::GetActiveUniformBlockiv::Result Result;
 | 
| +  Result* result = GetSharedMemoryAs<Result*>(
 | 
| +      c.params_shm_id, c.params_shm_offset, Result::ComputeSize(num_values));
 | 
| +  GLint* params = result ? result->GetData() : NULL;
 | 
| +  if (params == NULL) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +  // Check that the client initialized the result.
 | 
| +  if (result->size != 0) {
 | 
| +    return error::kInvalidArguments;
 | 
| +  }
 | 
| +  glGetActiveUniformBlockiv(service_id, index, pname, params);
 | 
| +  GLenum error = glGetError();
 | 
| +  if (error == GL_NO_ERROR) {
 | 
| +    result->SetNumResults(num_values);
 | 
| +  } else {
 | 
| +    LOCAL_SET_GL_ERROR(error, "GetActiveUniformBlockiv", "");
 | 
| +  }
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
|  error::Error GLES2DecoderImpl::HandleGetActiveUniformBlockName(
 | 
|      uint32 immediate_data_size, const void* cmd_data) {
 | 
|    if (!unsafe_es3_apis_enabled())
 | 
| @@ -9783,6 +9902,55 @@ error::Error GLES2DecoderImpl::HandleGetActiveUniformBlockName(
 | 
|    return error::kNoError;
 | 
|  }
 | 
|  
 | 
| +error::Error GLES2DecoderImpl::HandleGetActiveUniformsiv(
 | 
| +    uint32 immediate_data_size, const void* cmd_data) {
 | 
| +  if (!unsafe_es3_apis_enabled())
 | 
| +    return error::kUnknownCommand;
 | 
| +  const gles2::cmds::GetActiveUniformsiv& c =
 | 
| +      *static_cast<const gles2::cmds::GetActiveUniformsiv*>(cmd_data);
 | 
| +  GLuint program_id = c.program;
 | 
| +  GLenum pname = static_cast<GLenum>(c.pname);
 | 
| +  Bucket* bucket = GetBucket(c.indices_bucket_id);
 | 
| +  if (!bucket) {
 | 
| +    return error::kInvalidArguments;
 | 
| +  }
 | 
| +  GLsizei count = static_cast<GLsizei>(bucket->size() / sizeof(GLuint));
 | 
| +  const GLuint* indices = bucket->GetDataAs<const GLuint*>(0, bucket->size());
 | 
| +  typedef cmds::GetActiveUniformsiv::Result Result;
 | 
| +  Result* result = GetSharedMemoryAs<Result*>(
 | 
| +      c.params_shm_id, c.params_shm_offset, Result::ComputeSize(count));
 | 
| +  GLint* params = result ? result->GetData() : NULL;
 | 
| +  if (params == NULL) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +  // Check that the client initialized the result.
 | 
| +  if (result->size != 0) {
 | 
| +    return error::kInvalidArguments;
 | 
| +  }
 | 
| +  Program* program = GetProgramInfoNotShader(
 | 
| +      program_id, "glGetActiveUniformsiv");
 | 
| +  if (!program) {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  GLuint service_id = program->service_id();
 | 
| +  GLint link_status = GL_FALSE;
 | 
| +  glGetProgramiv(service_id, GL_LINK_STATUS, &link_status);
 | 
| +  if (link_status != GL_TRUE) {
 | 
| +    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
 | 
| +        "glGetActiveUniformsiv", "program not linked");
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetActiveUniformsiv");
 | 
| +  glGetActiveUniformsiv(service_id, count, indices, pname, params);
 | 
| +  GLenum error = glGetError();
 | 
| +  if (error == GL_NO_ERROR) {
 | 
| +    result->SetNumResults(count);
 | 
| +  } else {
 | 
| +    LOCAL_SET_GL_ERROR(error, "GetActiveUniformsiv", "");
 | 
| +  }
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
|  error::Error GLES2DecoderImpl::HandleGetActiveAttrib(uint32 immediate_data_size,
 | 
|                                                       const void* cmd_data) {
 | 
|    const gles2::cmds::GetActiveAttrib& c =
 | 
| @@ -10136,6 +10304,104 @@ error::Error GLES2DecoderImpl::HandleGetUniformBlocksCHROMIUM(
 | 
|    return error::kNoError;
 | 
|  }
 | 
|  
 | 
| +error::Error GLES2DecoderImpl::HandleGetUniformsES3CHROMIUM(
 | 
| +    uint32 immediate_data_size, const void* cmd_data) {
 | 
| +  if (!unsafe_es3_apis_enabled())
 | 
| +    return error::kUnknownCommand;
 | 
| +  const gles2::cmds::GetUniformsES3CHROMIUM& c =
 | 
| +      *static_cast<const gles2::cmds::GetUniformsES3CHROMIUM*>(cmd_data);
 | 
| +  GLuint program_id = static_cast<GLuint>(c.program);
 | 
| +  uint32 bucket_id = c.bucket_id;
 | 
| +  Bucket* bucket = CreateBucket(bucket_id);
 | 
| +  bucket->SetSize(sizeof(UniformsES3Header));  // in case we fail.
 | 
| +  Program* program = NULL;
 | 
| +  program = GetProgram(program_id);
 | 
| +  if (!program || !program->IsValid()) {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  program->GetUniformsES3(bucket);
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
| +error::Error GLES2DecoderImpl::HandleGetTransformFeedbackVarying(
 | 
| +    uint32 immediate_data_size,
 | 
| +    const void* cmd_data) {
 | 
| +  if (!unsafe_es3_apis_enabled())
 | 
| +    return error::kUnknownCommand;
 | 
| +  const gles2::cmds::GetTransformFeedbackVarying& c =
 | 
| +      *static_cast<const gles2::cmds::GetTransformFeedbackVarying*>(cmd_data);
 | 
| +  GLuint program_id = c.program;
 | 
| +  GLuint index = c.index;
 | 
| +  uint32 name_bucket_id = c.name_bucket_id;
 | 
| +  typedef cmds::GetTransformFeedbackVarying::Result Result;
 | 
| +  Result* result = GetSharedMemoryAs<Result*>(
 | 
| +      c.result_shm_id, c.result_shm_offset, sizeof(*result));
 | 
| +  if (!result) {
 | 
| +    return error::kOutOfBounds;
 | 
| +  }
 | 
| +  // Check that the client initialized the result.
 | 
| +  if (result->success != 0) {
 | 
| +    return error::kInvalidArguments;
 | 
| +  }
 | 
| +  Program* program = GetProgramInfoNotShader(
 | 
| +      program_id, "glGetTransformFeedbackVarying");
 | 
| +  if (!program) {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  GLuint service_id = program->service_id();
 | 
| +  GLint link_status = GL_FALSE;
 | 
| +  glGetProgramiv(service_id, GL_LINK_STATUS, &link_status);
 | 
| +  if (link_status != GL_TRUE) {
 | 
| +    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
 | 
| +        "glGetTransformFeedbackVarying", "program not linked");
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  GLint max_length = 0;
 | 
| +  glGetProgramiv(
 | 
| +      service_id, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &max_length);
 | 
| +  max_length = std::max(1, max_length);
 | 
| +  std::vector<char> buffer(max_length);
 | 
| +  GLsizei length = 0;
 | 
| +  GLsizei size = 0;
 | 
| +  GLenum type = 0;
 | 
| +  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetTransformFeedbackVarying");
 | 
| +  glGetTransformFeedbackVarying(
 | 
| +      service_id, index, max_length, &length, &size, &type, &buffer[0]);
 | 
| +  GLenum error = glGetError();
 | 
| +  if (error != GL_NO_ERROR) {
 | 
| +    LOCAL_SET_GL_ERROR(error, "glGetTransformFeedbackVarying", "");
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  result->success = 1;  // true.
 | 
| +  result->size = static_cast<int32_t>(size);
 | 
| +  result->type = static_cast<uint32_t>(type);
 | 
| +  Bucket* bucket = CreateBucket(name_bucket_id);
 | 
| +  DCHECK(length >= 0 && length < max_length);
 | 
| +  buffer[length] = '\0';  // Just to be safe.
 | 
| +  bucket->SetFromString(&buffer[0]);
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
| +error::Error GLES2DecoderImpl::HandleGetTransformFeedbackVaryingsCHROMIUM(
 | 
| +    uint32 immediate_data_size, const void* cmd_data) {
 | 
| +  if (!unsafe_es3_apis_enabled())
 | 
| +    return error::kUnknownCommand;
 | 
| +  const gles2::cmds::GetTransformFeedbackVaryingsCHROMIUM& c =
 | 
| +      *static_cast<const gles2::cmds::GetTransformFeedbackVaryingsCHROMIUM*>(
 | 
| +          cmd_data);
 | 
| +  GLuint program_id = static_cast<GLuint>(c.program);
 | 
| +  uint32 bucket_id = c.bucket_id;
 | 
| +  Bucket* bucket = CreateBucket(bucket_id);
 | 
| +  bucket->SetSize(sizeof(TransformFeedbackVaryingsHeader));  // in case we fail.
 | 
| +  Program* program = NULL;
 | 
| +  program = GetProgram(program_id);
 | 
| +  if (!program || !program->IsValid()) {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  program->GetTransformFeedbackVaryings(bucket);
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
|  error::ContextLostReason GLES2DecoderImpl::GetContextLostReason() {
 | 
|    switch (reset_status_) {
 | 
|      case GL_NO_ERROR:
 | 
| @@ -10172,10 +10438,8 @@ bool GLES2DecoderImpl::WasContextLost() {
 | 
|      MaybeExitOnContextLost();
 | 
|      return true;
 | 
|    }
 | 
| -  if (context_->WasAllocatedUsingRobustnessExtension()) {
 | 
| -    GLenum status = GL_NO_ERROR;
 | 
| -    if (has_robustness_extension_)
 | 
| -      status = glGetGraphicsResetStatusARB();
 | 
| +  if (IsRobustnessSupported()) {
 | 
| +    GLenum status = glGetGraphicsResetStatusARB();
 | 
|      if (status != GL_NO_ERROR) {
 | 
|        // The graphics card was reset. Signal a lost context to the application.
 | 
|        reset_status_ = status;
 | 
| @@ -10207,7 +10471,7 @@ void GLES2DecoderImpl::LoseContext(uint32 reset_status) {
 | 
|        reset_status = GL_UNKNOWN_CONTEXT_RESET_ARB;
 | 
|      }
 | 
|    } else if (reset_status == GL_UNKNOWN_CONTEXT_RESET_ARB &&
 | 
| -      has_robustness_extension_) {
 | 
| +             IsRobustnessSupported()) {
 | 
|      // If the reason for the call was a GL error, we can try to determine the
 | 
|      // reset status more accurately.
 | 
|      GLenum driver_status = glGetGraphicsResetStatusARB();
 | 
| @@ -11772,6 +12036,25 @@ error::Error GLES2DecoderImpl::HandleWaitAllAsyncTexImage2DCHROMIUM(
 | 
|    return error::kNoError;
 | 
|  }
 | 
|  
 | 
| +error::Error GLES2DecoderImpl::HandleUniformBlockBinding(
 | 
| +    uint32_t immediate_data_size, const void* cmd_data) {
 | 
| +  if (!unsafe_es3_apis_enabled())
 | 
| +    return error::kUnknownCommand;
 | 
| +  const gles2::cmds::UniformBlockBinding& c =
 | 
| +      *static_cast<const gles2::cmds::UniformBlockBinding*>(cmd_data);
 | 
| +  GLuint client_id = c.program;
 | 
| +  GLuint index = static_cast<GLuint>(c.index);
 | 
| +  GLuint binding = static_cast<GLuint>(c.binding);
 | 
| +  Program* program = GetProgramInfoNotShader(
 | 
| +      client_id, "glUniformBlockBinding");
 | 
| +  if (!program) {
 | 
| +    return error::kNoError;
 | 
| +  }
 | 
| +  GLuint service_id = program->service_id();
 | 
| +  glUniformBlockBinding(service_id, index, binding);
 | 
| +  return error::kNoError;
 | 
| +}
 | 
| +
 | 
|  void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer(
 | 
|      TextureRef* texture_ref) {
 | 
|    Texture* texture = texture_ref->texture();
 | 
| 
 |