| 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 f28cc4387e7832e07bdf8335f303ff646fd0293c..affed33b0e82a8399fc076eb5e414d73e347da4a 100644
 | 
| --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
 | 
| +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
 | 
| @@ -983,6 +983,12 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient {
 | 
|  
 | 
|    void DoCompressedCopyTextureCHROMIUM(GLuint source_id, GLuint dest_id);
 | 
|  
 | 
| +  // Helper for DoTexStorage2DEXT and DoTexStorage3D.
 | 
| +  void TexStorageImpl(
 | 
| +    GLenum target, GLint levels, GLenum internal_format,
 | 
| +    GLsizei width, GLsizei height, GLsizei depth,
 | 
| +    ContextState::Dimension dimension, const char* function_name);
 | 
| +
 | 
|    // Wrapper for TexStorage2DEXT.
 | 
|    void DoTexStorage2DEXT(
 | 
|        GLenum target,
 | 
| @@ -14298,143 +14304,65 @@ void GLES2DecoderImpl::DoCompressedCopyTextureCHROMIUM(GLuint source_id,
 | 
|        false, false);
 | 
|  }
 | 
|  
 | 
| -void GLES2DecoderImpl::DoTexStorage2DEXT(
 | 
| -    GLenum target,
 | 
| -    GLint levels,
 | 
| -    GLenum internal_format,
 | 
| -    GLsizei width,
 | 
| -    GLsizei height) {
 | 
| -  TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage2DEXT",
 | 
| -      "width", width, "height", height);
 | 
| -  if (!texture_manager()->ValidForTarget(target, 0, width, height, 1) ||
 | 
| -      TextureManager::ComputeMipMapCount(target, width, height, 1) < levels) {
 | 
| -    LOCAL_SET_GL_ERROR(
 | 
| -        GL_INVALID_VALUE, "glTexStorage2DEXT", "dimensions out of range");
 | 
| -    return;
 | 
| -  }
 | 
| -  TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget(
 | 
| -      &state_, 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()) {
 | 
| -    framebuffer_state_.clear_state_dirty = true;
 | 
| -  }
 | 
| -  if (texture->IsImmutable()) {
 | 
| -    LOCAL_SET_GL_ERROR(
 | 
| -        GL_INVALID_OPERATION,
 | 
| -        "glTexStorage2DEXT", "texture is immutable");
 | 
| -    return;
 | 
| -  }
 | 
| -
 | 
| -  GLenum format = TextureManager::ExtractFormatFromStorageFormat(
 | 
| -      internal_format);
 | 
| -  GLenum type = TextureManager::ExtractTypeFromStorageFormat(internal_format);
 | 
| -
 | 
| -  {
 | 
| -    GLsizei level_width = width;
 | 
| -    GLsizei level_height = height;
 | 
| -    uint32_t estimated_size = 0;
 | 
| -    for (int ii = 0; ii < levels; ++ii) {
 | 
| -      uint32_t level_size = 0;
 | 
| -      if (!GLES2Util::ComputeImageDataSizes(
 | 
| -          level_width, level_height, 1, format, type, state_.unpack_alignment,
 | 
| -          &estimated_size, NULL, NULL) ||
 | 
| -          !SafeAddUint32(estimated_size, level_size, &estimated_size)) {
 | 
| -        LOCAL_SET_GL_ERROR(
 | 
| -            GL_OUT_OF_MEMORY, "glTexStorage2DEXT", "dimensions too large");
 | 
| -        return;
 | 
| -      }
 | 
| -      level_width = std::max(1, level_width >> 1);
 | 
| -      level_height = std::max(1, level_height >> 1);
 | 
| -    }
 | 
| -    if (!EnsureGPUMemoryAvailable(estimated_size)) {
 | 
| -      LOCAL_SET_GL_ERROR(
 | 
| -          GL_OUT_OF_MEMORY, "glTexStorage2DEXT", "out of memory");
 | 
| +void GLES2DecoderImpl::TexStorageImpl(
 | 
| +    GLenum target, GLint levels, GLenum internal_format,
 | 
| +    GLsizei width, GLsizei height, GLsizei depth,
 | 
| +    ContextState::Dimension dimension, const char* function_name) {
 | 
| +  if (dimension == ContextState::k2D) {
 | 
| +    if (!validators_->texture_bind_target.IsValid(target)) {
 | 
| +      LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, target, "target");
 | 
|        return;
 | 
|      }
 | 
| -  }
 | 
| -
 | 
| -  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glTexStorage2DEXT");
 | 
| -  glTexStorage2DEXT(target, levels, internal_format, width, height);
 | 
| -  GLenum error = LOCAL_PEEK_GL_ERROR("glTexStorage2DEXT");
 | 
| -  if (error == GL_NO_ERROR) {
 | 
| -    GLsizei level_width = width;
 | 
| -    GLsizei level_height = height;
 | 
| -
 | 
| -    GLenum cur_format = feature_info_->IsES3Enabled() ?
 | 
| -                        internal_format : format;
 | 
| -    for (int ii = 0; ii < levels; ++ii) {
 | 
| -      if (target == GL_TEXTURE_CUBE_MAP) {
 | 
| -        for (int jj = 0; jj < 6; ++jj) {
 | 
| -          GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + jj;
 | 
| -          texture_manager()->SetLevelInfo(texture_ref, face, ii, cur_format,
 | 
| -                                          level_width, level_height, 1, 0,
 | 
| -                                          format, type, gfx::Rect());
 | 
| -        }
 | 
| -      } else {
 | 
| -        texture_manager()->SetLevelInfo(texture_ref, target, ii, cur_format,
 | 
| -                                        level_width, level_height, 1, 0,
 | 
| -                                        format, type, gfx::Rect());
 | 
| -      }
 | 
| -      level_width = std::max(1, level_width >> 1);
 | 
| -      level_height = std::max(1, level_height >> 1);
 | 
| +  } else {
 | 
| +    if (!validators_->texture_3_d_target.IsValid(target)) {
 | 
| +      LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, target, "target");
 | 
| +      return;
 | 
|      }
 | 
| -    texture->SetImmutable(true);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -void GLES2DecoderImpl::DoTexStorage3D(
 | 
| -    GLenum target,
 | 
| -    GLint levels,
 | 
| -    GLenum internal_format,
 | 
| -    GLsizei width,
 | 
| -    GLsizei height,
 | 
| -    GLsizei depth) {
 | 
| -  TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage3D",
 | 
| -      "widthXheight", width * height, "depth", depth);
 | 
| -  if (!validators_->texture_3_d_target.IsValid(target)) {
 | 
| -    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage3D", target, "target");
 | 
| -    return;
 | 
|    }
 | 
|    if (levels <= 0) {
 | 
| -    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage3D", "levels <= 0");
 | 
| +    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "levels <= 0");
 | 
|      return;
 | 
|    }
 | 
|    if (!validators_->texture_internal_format_storage.IsValid(internal_format)) {
 | 
| -    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage3D", internal_format,
 | 
| -                                    "internal_format");
 | 
| -    return;
 | 
| -  }
 | 
| -  if (width <= 0) {
 | 
| -    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage3D", "width <= 0");
 | 
| -    return;
 | 
| -  }
 | 
| -  if (height <= 0) {
 | 
| -    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage3D", "height <= 0");
 | 
| +    LOCAL_SET_GL_ERROR_INVALID_ENUM(
 | 
| +        function_name, internal_format, "internal_format");
 | 
|      return;
 | 
|    }
 | 
| -  if (depth <= 0) {
 | 
| -    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage3D", "depth <= 0");
 | 
| -    return;
 | 
| +  bool is_compressed_format;
 | 
| +  switch (internal_format) {
 | 
| +    case GL_COMPRESSED_R11_EAC:
 | 
| +    case GL_COMPRESSED_SIGNED_R11_EAC:
 | 
| +    case GL_COMPRESSED_RG11_EAC:
 | 
| +    case GL_COMPRESSED_SIGNED_RG11_EAC:
 | 
| +    case GL_COMPRESSED_RGB8_ETC2:
 | 
| +    case GL_COMPRESSED_SRGB8_ETC2:
 | 
| +    case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
 | 
| +    case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
 | 
| +    case GL_COMPRESSED_RGBA8_ETC2_EAC:
 | 
| +    case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
 | 
| +      is_compressed_format = true;
 | 
| +      if (target == GL_TEXTURE_3D) {
 | 
| +        LOCAL_SET_GL_ERROR(
 | 
| +            GL_INVALID_OPERATION, function_name, "target invalid for format");
 | 
| +        return;
 | 
| +      }
 | 
| +      break;
 | 
| +    default:
 | 
| +      is_compressed_format = false;
 | 
| +      break;
 | 
|    }
 | 
|    if (!texture_manager()->ValidForTarget(target, 0, width, height, depth) ||
 | 
|        TextureManager::ComputeMipMapCount(
 | 
|            target, width, height, depth) < levels) {
 | 
|      LOCAL_SET_GL_ERROR(
 | 
| -        GL_INVALID_VALUE, "glTexStorage3D", "dimensions out of range");
 | 
| +        GL_INVALID_VALUE, function_name, "dimensions out of range");
 | 
|      return;
 | 
|    }
 | 
|    TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget(
 | 
|        &state_, target);
 | 
|    if (!texture_ref) {
 | 
|      LOCAL_SET_GL_ERROR(
 | 
| -        GL_INVALID_OPERATION,
 | 
| -        "glTexStorage3D", "unknown texture for target");
 | 
| +        GL_INVALID_OPERATION, function_name, "unknown texture for target");
 | 
|      return;
 | 
|    }
 | 
|    Texture* texture = texture_ref->texture();
 | 
| @@ -14443,7 +14371,7 @@ void GLES2DecoderImpl::DoTexStorage3D(
 | 
|    }
 | 
|    if (texture->IsImmutable()) {
 | 
|      LOCAL_SET_GL_ERROR(
 | 
| -        GL_INVALID_OPERATION, "glTexStorage3D", "texture is immutable");
 | 
| +        GL_INVALID_OPERATION, function_name, "texture is immutable");
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| @@ -14451,48 +14379,81 @@ void GLES2DecoderImpl::DoTexStorage3D(
 | 
|        internal_format);
 | 
|    GLenum type = TextureManager::ExtractTypeFromStorageFormat(internal_format);
 | 
|  
 | 
| +  std::vector<int32_t> level_size(levels);
 | 
|    {
 | 
|      GLsizei level_width = width;
 | 
|      GLsizei level_height = height;
 | 
|      GLsizei level_depth = depth;
 | 
| -    uint32_t estimated_size = 0;
 | 
| +    base::CheckedNumeric<uint32_t> estimated_size(0);
 | 
| +    PixelStoreParams params;
 | 
| +    params.alignment = 1;
 | 
|      for (int ii = 0; ii < levels; ++ii) {
 | 
| -      uint32_t level_size = 0;
 | 
| -      if (!GLES2Util::ComputeImageDataSizes(
 | 
| -          level_width, level_height, level_depth, format, type,
 | 
| -          state_.unpack_alignment,
 | 
| -          &estimated_size, NULL, NULL) ||
 | 
| -          !SafeAddUint32(estimated_size, level_size, &estimated_size)) {
 | 
| -        LOCAL_SET_GL_ERROR(
 | 
| -            GL_OUT_OF_MEMORY, "glTexStorage3D", "dimensions too large");
 | 
| -        return;
 | 
| +      uint32_t size;
 | 
| +      if (is_compressed_format) {
 | 
| +        if (!GetCompressedTexSizeInBytes(function_name,
 | 
| +                                         level_width, level_height, level_depth,
 | 
| +                                         internal_format, &level_size[ii])) {
 | 
| +          // GetCompressedTexSizeInBytes() already generates a GL error.
 | 
| +          return;
 | 
| +        }
 | 
| +        size = static_cast<uint32_t>(level_size[ii]);
 | 
| +      } else {
 | 
| +        if (!GLES2Util::ComputeImageDataSizesES3(level_width,
 | 
| +                                                 level_height,
 | 
| +                                                 level_depth,
 | 
| +                                                 format, type,
 | 
| +                                                 params,
 | 
| +                                                 &size,
 | 
| +                                                 nullptr, nullptr,
 | 
| +                                                 nullptr, nullptr)) {
 | 
| +          LOCAL_SET_GL_ERROR(
 | 
| +              GL_OUT_OF_MEMORY, function_name, "dimensions too large");
 | 
| +          return;
 | 
| +        }
 | 
|        }
 | 
| +      estimated_size += size;
 | 
|        level_width = std::max(1, level_width >> 1);
 | 
|        level_height = std::max(1, level_height >> 1);
 | 
|        if (target == GL_TEXTURE_3D)
 | 
|          level_depth = std::max(1, level_depth >> 1);
 | 
|      }
 | 
| -    if (!EnsureGPUMemoryAvailable(estimated_size)) {
 | 
| -      LOCAL_SET_GL_ERROR(
 | 
| -          GL_OUT_OF_MEMORY, "glTexStorage3D", "out of memory");
 | 
| +    if (!estimated_size.IsValid() ||
 | 
| +        !EnsureGPUMemoryAvailable(estimated_size.ValueOrDefault(0))) {
 | 
| +      LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, function_name, "out of memory");
 | 
|        return;
 | 
|      }
 | 
|    }
 | 
|  
 | 
| -  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glTexStorage3D");
 | 
| -  glTexStorage3D(target, levels, internal_format, width, height, depth);
 | 
| -  GLenum error = LOCAL_PEEK_GL_ERROR("glTexStorage3D");
 | 
| -  if (error == GL_NO_ERROR) {
 | 
| +  // TODO(zmo): We might need to emulate TexStorage using TexImage or
 | 
| +  // CompressedTexImage on Mac OSX where we expose ES3 APIs when the underlying
 | 
| +  // driver is lower than 4.2 and ARB_texture_storage extension doesn't exist.
 | 
| +  if (dimension == ContextState::k2D) {
 | 
| +    glTexStorage2DEXT(target, levels, internal_format, width, height);
 | 
| +  } else {
 | 
| +    glTexStorage3D(target, levels, internal_format, width, height, depth);
 | 
| +  }
 | 
| +
 | 
| +  {
 | 
|      GLsizei level_width = width;
 | 
|      GLsizei level_height = height;
 | 
|      GLsizei level_depth = depth;
 | 
| -
 | 
| -    GLenum cur_format = feature_info_->IsES3Enabled() ?
 | 
| -                        internal_format : format;
 | 
| +    GLenum adjusted_format =
 | 
| +        feature_info_->IsES3Enabled() ? internal_format : format;
 | 
|      for (int ii = 0; ii < levels; ++ii) {
 | 
| -      texture_manager()->SetLevelInfo(texture_ref, target, ii, cur_format,
 | 
| -                                      level_width, level_height, level_depth, 0,
 | 
| -                                      format, type, gfx::Rect());
 | 
| +      if (target == GL_TEXTURE_CUBE_MAP) {
 | 
| +        for (int jj = 0; jj < 6; ++jj) {
 | 
| +          GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + jj;
 | 
| +          texture_manager()->SetLevelInfo(texture_ref, face, ii,
 | 
| +                                          adjusted_format,
 | 
| +                                          level_width, level_height, 1,
 | 
| +                                          0, format, type, gfx::Rect());
 | 
| +        }
 | 
| +      } else {
 | 
| +        texture_manager()->SetLevelInfo(texture_ref, target, ii,
 | 
| +                                        adjusted_format,
 | 
| +                                        level_width, level_height, level_depth,
 | 
| +                                        0, format, type, gfx::Rect());
 | 
| +      }
 | 
|        level_width = std::max(1, level_width >> 1);
 | 
|        level_height = std::max(1, level_height >> 1);
 | 
|        if (target == GL_TEXTURE_3D)
 | 
| @@ -14502,6 +14463,31 @@ void GLES2DecoderImpl::DoTexStorage3D(
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +void GLES2DecoderImpl::DoTexStorage2DEXT(
 | 
| +    GLenum target,
 | 
| +    GLint levels,
 | 
| +    GLenum internal_format,
 | 
| +    GLsizei width,
 | 
| +    GLsizei height) {
 | 
| +  TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage2D",
 | 
| +      "width", width, "height", height);
 | 
| +  TexStorageImpl(target, levels, internal_format, width, height, 1,
 | 
| +                 ContextState::k2D, "glTexStorage2D");
 | 
| +}
 | 
| +
 | 
| +void GLES2DecoderImpl::DoTexStorage3D(
 | 
| +    GLenum target,
 | 
| +    GLint levels,
 | 
| +    GLenum internal_format,
 | 
| +    GLsizei width,
 | 
| +    GLsizei height,
 | 
| +    GLsizei depth) {
 | 
| +  TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage3D",
 | 
| +      "widthXheight", width * height, "depth", depth);
 | 
| +  TexStorageImpl(target, levels, internal_format, width, height, depth,
 | 
| +                 ContextState::k3D, "glTexStorage3D");
 | 
| +}
 | 
| +
 | 
|  error::Error GLES2DecoderImpl::HandleGenMailboxCHROMIUM(
 | 
|      uint32_t immediate_data_size,
 | 
|      const void* cmd_data) {
 | 
| 
 |