|  | // 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/command_buffer/client/program_info_manager.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template<typename T> static T LocalGetAs( | 
|  | const std::vector<int8>& data, uint32 offset, size_t size) { | 
|  | const int8* p = &data[0] + offset; | 
|  | if (offset + size > data.size()) { | 
|  | NOTREACHED(); | 
|  | return NULL; | 
|  | } | 
|  | return static_cast<T>(static_cast<const void*>(p)); | 
|  | } | 
|  |  | 
|  | }  // namespace anonymous | 
|  |  | 
|  | namespace gpu { | 
|  | namespace gles2 { | 
|  |  | 
|  | ProgramInfoManager::Program::VertexAttrib::VertexAttrib( | 
|  | GLsizei _size, GLenum _type, const std::string& _name, GLint _location) | 
|  | : size(_size), | 
|  | type(_type), | 
|  | location(_location), | 
|  | name(_name) { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::VertexAttrib::~VertexAttrib() { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::UniformInfo::UniformInfo( | 
|  | GLsizei _size, GLenum _type, const std::string& _name) | 
|  | : size(_size), | 
|  | type(_type), | 
|  | name(_name) { | 
|  | is_array = (!name.empty() && name[name.size() - 1] == ']'); | 
|  | DCHECK(!(size > 1 && !is_array)); | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::UniformInfo::~UniformInfo() { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::UniformES3::UniformES3() | 
|  | : block_index(-1), | 
|  | offset(-1), | 
|  | array_stride(-1), | 
|  | matrix_stride(-1), | 
|  | is_row_major(0) { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::UniformES3::~UniformES3() { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::UniformBlock::UniformBlock() | 
|  | : binding(0), | 
|  | data_size(0), | 
|  | referenced_by_vertex_shader(false), | 
|  | referenced_by_fragment_shader(false) { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::UniformBlock::~UniformBlock() { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::TransformFeedbackVarying:: | 
|  | TransformFeedbackVarying() | 
|  | : size(0), | 
|  | type(0) { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::TransformFeedbackVarying:: | 
|  | ~TransformFeedbackVarying() { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::Program() | 
|  | : cached_es2_(false), | 
|  | max_attrib_name_length_(0), | 
|  | max_uniform_name_length_(0), | 
|  | link_status_(false), | 
|  | cached_es3_uniform_blocks_(false), | 
|  | active_uniform_block_max_name_length_(0), | 
|  | cached_es3_transform_feedback_varyings_(false), | 
|  | transform_feedback_varying_max_length_(0), | 
|  | transform_feedback_buffer_mode_(0), | 
|  | cached_es3_uniformsiv_(false) { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program::~Program() { | 
|  | } | 
|  |  | 
|  | // TODO(gman): Add a faster lookup. | 
|  | GLint ProgramInfoManager::Program::GetAttribLocation( | 
|  | const std::string& name) const { | 
|  | for (GLuint ii = 0; ii < attrib_infos_.size(); ++ii) { | 
|  | const VertexAttrib& info = attrib_infos_[ii]; | 
|  | if (info.name == name) { | 
|  | return info.location; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | const ProgramInfoManager::Program::VertexAttrib* | 
|  | ProgramInfoManager::Program::GetAttribInfo(GLint index) const { | 
|  | return (static_cast<size_t>(index) < attrib_infos_.size()) ? | 
|  | &attrib_infos_[index] : NULL; | 
|  | } | 
|  |  | 
|  | const ProgramInfoManager::Program::UniformInfo* | 
|  | ProgramInfoManager::Program::GetUniformInfo(GLint index) const { | 
|  | return (static_cast<size_t>(index) < uniform_infos_.size()) ? | 
|  | &uniform_infos_[index] : NULL; | 
|  | } | 
|  |  | 
|  | const ProgramInfoManager::Program::UniformBlock* | 
|  | ProgramInfoManager::Program::GetUniformBlock(GLuint index) const { | 
|  | return (index < uniform_blocks_.size()) ? &uniform_blocks_[index] : NULL; | 
|  | } | 
|  |  | 
|  | GLint ProgramInfoManager::Program::GetUniformLocation( | 
|  | const std::string& name) const { | 
|  | GLSLArrayName parsed_name(name); | 
|  |  | 
|  | for (GLuint ii = 0; ii < uniform_infos_.size(); ++ii) { | 
|  | const UniformInfo& info = uniform_infos_[ii]; | 
|  | if (info.name == name || | 
|  | (info.is_array && | 
|  | info.name.compare(0, info.name.size() - 3, name) == 0)) { | 
|  | return info.element_locations[0]; | 
|  | } else if (parsed_name.IsArrayName() && info.is_array) { | 
|  | // Look for an array specification. | 
|  | size_t open_pos = info.name.find_last_of('['); | 
|  | if (info.name.compare(0, open_pos, parsed_name.base_name()) == 0) { | 
|  | int index = parsed_name.element_index(); | 
|  | if (index < info.size) { | 
|  | return info.element_locations[index]; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | GLuint ProgramInfoManager::Program::GetUniformIndex( | 
|  | const std::string& name) const { | 
|  | // TODO(zmo): Maybe build a hashed_map for faster lookup. | 
|  | for (GLuint ii = 0; ii < uniform_infos_.size(); ++ii) { | 
|  | const UniformInfo& info = uniform_infos_[ii]; | 
|  | // For an array, either "var" or "var[0]" is considered as a match. | 
|  | // See "OpenGL ES 3.0.0, Section 2.11.3 Program Objects." | 
|  | if (info.name == name || | 
|  | (info.is_array && | 
|  | info.name.compare(0, info.name.size() - 3, name) == 0)) { | 
|  | return ii; | 
|  | } | 
|  | } | 
|  | return GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | GLint ProgramInfoManager::Program::GetFragDataIndex( | 
|  | const std::string& name) const { | 
|  | auto iter = frag_data_indices_.find(name); | 
|  | if (iter == frag_data_indices_.end()) | 
|  | return -1; | 
|  | return iter->second; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::Program::CacheFragDataIndex(const std::string& name, | 
|  | GLint index) { | 
|  | frag_data_indices_[name] = index; | 
|  | } | 
|  |  | 
|  | GLint ProgramInfoManager::Program::GetFragDataLocation( | 
|  | const std::string& name) const { | 
|  | base::hash_map<std::string, GLint>::const_iterator iter = | 
|  | frag_data_locations_.find(name); | 
|  | if (iter == frag_data_locations_.end()) | 
|  | return -1; | 
|  | return iter->second; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::Program::CacheFragDataLocation( | 
|  | const std::string& name, GLint loc) { | 
|  | frag_data_locations_[name] = loc; | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::Program::GetProgramiv( | 
|  | GLenum pname, GLint* params) { | 
|  | switch (pname) { | 
|  | case GL_LINK_STATUS: | 
|  | *params = static_cast<GLint>(link_status_); | 
|  | return true; | 
|  | case GL_ACTIVE_ATTRIBUTES: | 
|  | *params = static_cast<GLint>(attrib_infos_.size()); | 
|  | return true; | 
|  | case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: | 
|  | *params = static_cast<GLint>(max_attrib_name_length_); | 
|  | return true; | 
|  | case GL_ACTIVE_UNIFORMS: | 
|  | *params = static_cast<GLint>(uniform_infos_.size()); | 
|  | return true; | 
|  | case GL_ACTIVE_UNIFORM_MAX_LENGTH: | 
|  | *params = static_cast<GLint>(max_uniform_name_length_); | 
|  | return true; | 
|  | case GL_ACTIVE_UNIFORM_BLOCKS: | 
|  | *params = static_cast<GLint>(uniform_blocks_.size()); | 
|  | return true; | 
|  | case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: | 
|  | *params = static_cast<GLint>(active_uniform_block_max_name_length_); | 
|  | return true; | 
|  | case GL_TRANSFORM_FEEDBACK_VARYINGS: | 
|  | *params = static_cast<GLint>(transform_feedback_varyings_.size()); | 
|  | return true; | 
|  | case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: | 
|  | *params = static_cast<GLint>(transform_feedback_varying_max_length_); | 
|  | return true; | 
|  | case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: | 
|  | *params = static_cast<GLint>(transform_feedback_buffer_mode_); | 
|  | return true; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GLuint ProgramInfoManager::Program::GetUniformBlockIndex( | 
|  | const std::string& name) const { | 
|  | for (size_t ii = 0; ii < uniform_blocks_.size(); ++ii) { | 
|  | if (uniform_blocks_[ii].name == name) { | 
|  | return static_cast<GLuint>(ii); | 
|  | } | 
|  | } | 
|  | return GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::Program::UniformBlockBinding( | 
|  | GLuint index , GLuint binding) { | 
|  | if (index < uniform_blocks_.size()) { | 
|  | uniform_blocks_[index].binding = binding; | 
|  | } | 
|  | } | 
|  |  | 
|  | const ProgramInfoManager::Program::TransformFeedbackVarying* | 
|  | ProgramInfoManager::Program::GetTransformFeedbackVarying(GLuint index) const { | 
|  | return (index < transform_feedback_varyings_.size()) ? | 
|  | &transform_feedback_varyings_[index] : NULL; | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::Program::GetUniformsiv( | 
|  | GLsizei count, const GLuint* indices, GLenum pname, GLint* params) { | 
|  | if (count == 0) { | 
|  | // At this point, pname has already been validated. | 
|  | return true; | 
|  | } | 
|  | DCHECK(count > 0 && indices); | 
|  | size_t num_uniforms = uniform_infos_.size(); | 
|  | if (num_uniforms == 0) { | 
|  | num_uniforms = uniforms_es3_.size(); | 
|  | } | 
|  | if (static_cast<size_t>(count) > num_uniforms) { | 
|  | return false; | 
|  | } | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | if (indices[ii] >= num_uniforms) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | if (!params) { | 
|  | return true; | 
|  | } | 
|  | switch (pname) { | 
|  | case GL_UNIFORM_SIZE: | 
|  | DCHECK_EQ(num_uniforms, uniform_infos_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = static_cast<GLint>(uniform_infos_[indices[ii]].size); | 
|  | } | 
|  | return true; | 
|  | case GL_UNIFORM_TYPE: | 
|  | DCHECK_EQ(num_uniforms, uniform_infos_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = static_cast<GLint>(uniform_infos_[indices[ii]].type); | 
|  | } | 
|  | return true; | 
|  | case GL_UNIFORM_NAME_LENGTH: | 
|  | DCHECK_EQ(num_uniforms, uniform_infos_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = static_cast<GLint>( | 
|  | uniform_infos_[indices[ii]].name.length() + 1); | 
|  | } | 
|  | return true; | 
|  | case GL_UNIFORM_BLOCK_INDEX: | 
|  | DCHECK_EQ(num_uniforms, uniforms_es3_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = uniforms_es3_[indices[ii]].block_index; | 
|  | } | 
|  | return true; | 
|  | case GL_UNIFORM_OFFSET: | 
|  | DCHECK_EQ(num_uniforms, uniforms_es3_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = uniforms_es3_[indices[ii]].offset; | 
|  | } | 
|  | return true; | 
|  | case GL_UNIFORM_ARRAY_STRIDE: | 
|  | DCHECK_EQ(num_uniforms, uniforms_es3_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = uniforms_es3_[indices[ii]].array_stride; | 
|  | } | 
|  | return true; | 
|  | case GL_UNIFORM_MATRIX_STRIDE: | 
|  | DCHECK_EQ(num_uniforms, uniforms_es3_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = uniforms_es3_[indices[ii]].matrix_stride; | 
|  | } | 
|  | return true; | 
|  | case GL_UNIFORM_IS_ROW_MAJOR: | 
|  | DCHECK_EQ(num_uniforms, uniforms_es3_.size()); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = uniforms_es3_[indices[ii]].is_row_major; | 
|  | } | 
|  | return true; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::Program::UpdateES2(const std::vector<int8>& result) { | 
|  | if (cached_es2_) { | 
|  | return; | 
|  | } | 
|  | if (result.empty()) { | 
|  | // This should only happen on a lost context. | 
|  | return; | 
|  | } | 
|  | DCHECK_GE(result.size(), sizeof(ProgramInfoHeader)); | 
|  | const ProgramInfoHeader* header = LocalGetAs<const ProgramInfoHeader*>( | 
|  | result, 0, sizeof(header)); | 
|  | link_status_ = header->link_status != 0; | 
|  | if (!link_status_) { | 
|  | return; | 
|  | } | 
|  | DCHECK_EQ(0u, attrib_infos_.size()); | 
|  | DCHECK_EQ(0u, uniform_infos_.size()); | 
|  | DCHECK_EQ(0, max_attrib_name_length_); | 
|  | DCHECK_EQ(0, max_uniform_name_length_); | 
|  | const ProgramInput* inputs = LocalGetAs<const ProgramInput*>( | 
|  | result, sizeof(*header), | 
|  | sizeof(ProgramInput) * (header->num_attribs + header->num_uniforms)); | 
|  | const ProgramInput* input = inputs; | 
|  | for (uint32 ii = 0; ii < header->num_attribs; ++ii) { | 
|  | const int32* location = LocalGetAs<const int32*>( | 
|  | result, input->location_offset, sizeof(int32)); | 
|  | const char* name_buf = LocalGetAs<const char*>( | 
|  | result, input->name_offset, input->name_length); | 
|  | std::string name(name_buf, input->name_length); | 
|  | attrib_infos_.push_back( | 
|  | VertexAttrib(input->size, input->type, name, *location)); | 
|  | max_attrib_name_length_ = std::max( | 
|  | static_cast<GLsizei>(name.size() + 1), max_attrib_name_length_); | 
|  | ++input; | 
|  | } | 
|  | for (uint32 ii = 0; ii < header->num_uniforms; ++ii) { | 
|  | const int32* locations = LocalGetAs<const int32*>( | 
|  | result, input->location_offset, sizeof(int32) * input->size); | 
|  | const char* name_buf = LocalGetAs<const char*>( | 
|  | result, input->name_offset, input->name_length); | 
|  | std::string name(name_buf, input->name_length); | 
|  | UniformInfo info(input->size, input->type, name); | 
|  | max_uniform_name_length_ = std::max( | 
|  | static_cast<GLsizei>(name.size() + 1), max_uniform_name_length_); | 
|  | for (int32 jj = 0; jj < input->size; ++jj) { | 
|  | info.element_locations.push_back(locations[jj]); | 
|  | } | 
|  | uniform_infos_.push_back(info); | 
|  | ++input; | 
|  | } | 
|  | DCHECK_EQ(header->num_attribs + header->num_uniforms, | 
|  | static_cast<uint32>(input - inputs)); | 
|  | cached_es2_ = true; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::Program::UpdateES3UniformBlocks( | 
|  | const std::vector<int8>& result) { | 
|  | if (cached_es3_uniform_blocks_) { | 
|  | return; | 
|  | } | 
|  | if (result.empty()) { | 
|  | // This should only happen on a lost context. | 
|  | return; | 
|  | } | 
|  | DCHECK_EQ(0u, uniform_blocks_.size()); | 
|  | DCHECK_EQ(0u, active_uniform_block_max_name_length_); | 
|  |  | 
|  | // |result| comes from GPU process. We consider it trusted data. Therefore, | 
|  | // no need to check for overflows as the GPU side did the checks already. | 
|  | uint32_t header_size = sizeof(UniformBlocksHeader); | 
|  | DCHECK_GE(result.size(), header_size); | 
|  | const UniformBlocksHeader* header = LocalGetAs<const UniformBlocksHeader*>( | 
|  | result, 0, header_size); | 
|  | DCHECK(header); | 
|  | if (header->num_uniform_blocks == 0) { | 
|  | DCHECK_EQ(result.size(), header_size); | 
|  | // TODO(zmo): Here we can't tell if no uniform blocks are defined, or | 
|  | // the previous link failed. | 
|  | return; | 
|  | } | 
|  | uniform_blocks_.resize(header->num_uniform_blocks); | 
|  |  | 
|  | uint32_t entry_size = sizeof(UniformBlockInfo) * header->num_uniform_blocks; | 
|  | DCHECK_GE(result.size(), header_size + entry_size); | 
|  | uint32_t data_size = result.size() - header_size - entry_size; | 
|  | DCHECK_LT(0u, data_size); | 
|  | const UniformBlockInfo* entries = LocalGetAs<const UniformBlockInfo*>( | 
|  | result, header_size, entry_size); | 
|  | DCHECK(entries); | 
|  | const char* data = LocalGetAs<const char*>( | 
|  | result, header_size + entry_size, data_size); | 
|  | DCHECK(data); | 
|  |  | 
|  | uint32_t size = 0; | 
|  | for (uint32_t ii = 0; ii < header->num_uniform_blocks; ++ii) { | 
|  | uniform_blocks_[ii].binding = static_cast<GLuint>(entries[ii].binding); | 
|  | uniform_blocks_[ii].data_size = static_cast<GLuint>(entries[ii].data_size); | 
|  | uniform_blocks_[ii].active_uniform_indices.resize( | 
|  | entries[ii].active_uniforms); | 
|  | uniform_blocks_[ii].referenced_by_vertex_shader = static_cast<GLboolean>( | 
|  | entries[ii].referenced_by_vertex_shader); | 
|  | uniform_blocks_[ii].referenced_by_fragment_shader = static_cast<GLboolean>( | 
|  | entries[ii].referenced_by_fragment_shader); | 
|  | // Uniform block names can't be empty strings. | 
|  | DCHECK_LT(1u, entries[ii].name_length); | 
|  | if (entries[ii].name_length > active_uniform_block_max_name_length_) { | 
|  | active_uniform_block_max_name_length_ = entries[ii].name_length; | 
|  | } | 
|  | size += entries[ii].name_length; | 
|  | DCHECK_GE(data_size, size); | 
|  | uniform_blocks_[ii].name = std::string(data, entries[ii].name_length - 1); | 
|  | data += entries[ii].name_length; | 
|  | size += entries[ii].active_uniforms * sizeof(uint32_t); | 
|  | DCHECK_GE(data_size, size); | 
|  | const uint32_t* indices = reinterpret_cast<const uint32_t*>(data); | 
|  | for (uint32_t uu = 0; uu < entries[ii].active_uniforms; ++uu) { | 
|  | uniform_blocks_[ii].active_uniform_indices[uu] = | 
|  | static_cast<GLuint>(indices[uu]); | 
|  | } | 
|  | indices += entries[ii].active_uniforms; | 
|  | data = reinterpret_cast<const char*>(indices); | 
|  | } | 
|  | DCHECK_EQ(data_size, size); | 
|  | cached_es3_uniform_blocks_ = true; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::Program::UpdateES3Uniformsiv( | 
|  | const std::vector<int8>& result) { | 
|  | if (cached_es3_uniformsiv_) { | 
|  | return; | 
|  | } | 
|  | if (result.empty()) { | 
|  | // This should only happen on a lost context. | 
|  | return; | 
|  | } | 
|  | DCHECK_EQ(0u, uniforms_es3_.size()); | 
|  |  | 
|  | // |result| comes from GPU process. We consider it trusted data. Therefore, | 
|  | // no need to check for overflows as the GPU side did the checks already. | 
|  | uint32_t header_size = sizeof(UniformsES3Header); | 
|  | DCHECK_GE(result.size(), header_size); | 
|  | const UniformsES3Header* header = LocalGetAs<const UniformsES3Header*>( | 
|  | result, 0, header_size); | 
|  | DCHECK(header); | 
|  | if (header->num_uniforms == 0) { | 
|  | DCHECK_EQ(result.size(), header_size); | 
|  | // TODO(zmo): Here we can't tell if no uniforms are defined, or | 
|  | // the previous link failed. | 
|  | return; | 
|  | } | 
|  | uniforms_es3_.resize(header->num_uniforms); | 
|  |  | 
|  | uint32_t entry_size = sizeof(UniformES3Info) * header->num_uniforms; | 
|  | DCHECK_EQ(result.size(), header_size + entry_size); | 
|  | const UniformES3Info* entries = LocalGetAs<const UniformES3Info*>( | 
|  | result, header_size, entry_size); | 
|  | DCHECK(entries); | 
|  |  | 
|  | for (uint32_t ii = 0; ii < header->num_uniforms; ++ii) { | 
|  | uniforms_es3_[ii].block_index = entries[ii].block_index; | 
|  | uniforms_es3_[ii].offset = entries[ii].offset; | 
|  | uniforms_es3_[ii].array_stride = entries[ii].array_stride; | 
|  | uniforms_es3_[ii].matrix_stride = entries[ii].matrix_stride; | 
|  | uniforms_es3_[ii].is_row_major = entries[ii].is_row_major; | 
|  | } | 
|  | cached_es3_uniformsiv_ = true; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::Program::UpdateES3TransformFeedbackVaryings( | 
|  | const std::vector<int8>& result) { | 
|  | if (cached_es3_transform_feedback_varyings_) { | 
|  | return; | 
|  | } | 
|  | if (result.empty()) { | 
|  | // This should only happen on a lost context. | 
|  | return; | 
|  | } | 
|  | DCHECK_EQ(0u, transform_feedback_buffer_mode_); | 
|  | DCHECK_EQ(0u, transform_feedback_varyings_.size()); | 
|  | DCHECK_EQ(0u, transform_feedback_varying_max_length_); | 
|  |  | 
|  | // |result| comes from GPU process. We consider it trusted data. Therefore, | 
|  | // no need to check for overflows as the GPU side did the checks already. | 
|  | uint32_t header_size = sizeof(TransformFeedbackVaryingsHeader); | 
|  | DCHECK_GE(result.size(), header_size); | 
|  | const TransformFeedbackVaryingsHeader* header = | 
|  | LocalGetAs<const TransformFeedbackVaryingsHeader*>( | 
|  | result, 0, header_size); | 
|  | DCHECK(header); | 
|  | if (header->num_transform_feedback_varyings == 0) { | 
|  | DCHECK_EQ(result.size(), header_size); | 
|  | // TODO(zmo): Here we can't tell if no TransformFeedback varyings are | 
|  | // defined, or the previous link failed. | 
|  | return; | 
|  | } | 
|  | transform_feedback_varyings_.resize(header->num_transform_feedback_varyings); | 
|  | transform_feedback_buffer_mode_ = header->transform_feedback_buffer_mode; | 
|  |  | 
|  | uint32_t entry_size = sizeof(TransformFeedbackVaryingInfo) * | 
|  | header->num_transform_feedback_varyings; | 
|  | DCHECK_GE(result.size(), header_size + entry_size); | 
|  | uint32_t data_size = result.size() - header_size - entry_size; | 
|  | DCHECK_LT(0u, data_size); | 
|  | const TransformFeedbackVaryingInfo* entries = | 
|  | LocalGetAs<const TransformFeedbackVaryingInfo*>( | 
|  | result, header_size, entry_size); | 
|  | DCHECK(entries); | 
|  | const char* data = LocalGetAs<const char*>( | 
|  | result, header_size + entry_size, data_size); | 
|  | DCHECK(data); | 
|  |  | 
|  | uint32_t size = 0; | 
|  | for (uint32_t ii = 0; ii < header->num_transform_feedback_varyings; ++ii) { | 
|  | transform_feedback_varyings_[ii].size = | 
|  | static_cast<GLsizei>(entries[ii].size); | 
|  | transform_feedback_varyings_[ii].type = | 
|  | static_cast<GLenum>(entries[ii].type); | 
|  | DCHECK_LE(1u, entries[ii].name_length); | 
|  | if (entries[ii].name_length > transform_feedback_varying_max_length_) { | 
|  | transform_feedback_varying_max_length_ = entries[ii].name_length; | 
|  | } | 
|  | size += entries[ii].name_length; | 
|  | DCHECK_GE(data_size, size); | 
|  | transform_feedback_varyings_[ii].name = | 
|  | std::string(data, entries[ii].name_length - 1); | 
|  | data += entries[ii].name_length; | 
|  | } | 
|  | DCHECK_EQ(data_size, size); | 
|  | cached_es3_transform_feedback_varyings_ = true; | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::Program::IsCached(ProgramInfoType type) const { | 
|  | switch (type) { | 
|  | case kES2: | 
|  | return cached_es2_; | 
|  | case kES3UniformBlocks: | 
|  | return cached_es3_uniform_blocks_; | 
|  | case kES3TransformFeedbackVaryings: | 
|  | return cached_es3_transform_feedback_varyings_; | 
|  | case kES3Uniformsiv: | 
|  | return cached_es3_uniformsiv_; | 
|  | case kNone: | 
|  | return true; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | ProgramInfoManager::ProgramInfoManager() { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::~ProgramInfoManager() { | 
|  | } | 
|  |  | 
|  | ProgramInfoManager::Program* ProgramInfoManager::GetProgramInfo( | 
|  | GLES2Implementation* gl, GLuint program, ProgramInfoType type) { | 
|  | lock_.AssertAcquired(); | 
|  | ProgramInfoMap::iterator it = program_infos_.find(program); | 
|  | if (it == program_infos_.end()) { | 
|  | return NULL; | 
|  | } | 
|  | Program* info = &it->second; | 
|  | if (info->IsCached(type)) | 
|  | return info; | 
|  |  | 
|  | std::vector<int8> result; | 
|  | switch (type) { | 
|  | case kES2: | 
|  | { | 
|  | base::AutoUnlock unlock(lock_); | 
|  | // lock_ can't be held across IPC call or else it may deadlock in | 
|  | // pepper. http://crbug.com/418651 | 
|  | gl->GetProgramInfoCHROMIUMHelper(program, &result); | 
|  | } | 
|  | info->UpdateES2(result); | 
|  | break; | 
|  | case kES3UniformBlocks: | 
|  | { | 
|  | base::AutoUnlock unlock(lock_); | 
|  | // lock_ can't be held across IPC call or else it may deadlock in | 
|  | // pepper. http://crbug.com/418651 | 
|  | gl->GetUniformBlocksCHROMIUMHelper(program, &result); | 
|  | } | 
|  | info->UpdateES3UniformBlocks(result); | 
|  | break; | 
|  | case kES3TransformFeedbackVaryings: | 
|  | { | 
|  | base::AutoUnlock unlock(lock_); | 
|  | // lock_ can't be held across IPC call or else it may deadlock in | 
|  | // pepper. http://crbug.com/418651 | 
|  | gl->GetTransformFeedbackVaryingsCHROMIUMHelper(program, &result); | 
|  | } | 
|  | info->UpdateES3TransformFeedbackVaryings(result); | 
|  | break; | 
|  | case kES3Uniformsiv: | 
|  | { | 
|  | base::AutoUnlock unlock(lock_); | 
|  | // lock_ can't be held across IPC call or else it may deadlock in | 
|  | // pepper. http://crbug.com/418651 | 
|  | gl->GetUniformsES3CHROMIUMHelper(program, &result); | 
|  | } | 
|  | info->UpdateES3Uniformsiv(result); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return NULL; | 
|  | } | 
|  | return info; | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::CreateInfo(GLuint program) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | program_infos_.erase(program); | 
|  | std::pair<ProgramInfoMap::iterator, bool> result = | 
|  | program_infos_.insert(std::make_pair(program, Program())); | 
|  |  | 
|  | DCHECK(result.second); | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::DeleteInfo(GLuint program) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | program_infos_.erase(program); | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetProgramiv( | 
|  | GLES2Implementation* gl, GLuint program, GLenum pname, GLint* params) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | ProgramInfoType type = kNone; | 
|  | switch (pname) { | 
|  | case GL_ACTIVE_ATTRIBUTES: | 
|  | case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: | 
|  | case GL_ACTIVE_UNIFORMS: | 
|  | case GL_ACTIVE_UNIFORM_MAX_LENGTH: | 
|  | case GL_LINK_STATUS: | 
|  | type = kES2; | 
|  | break; | 
|  | case GL_ACTIVE_UNIFORM_BLOCKS: | 
|  | case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: | 
|  | type = kES3UniformBlocks; | 
|  | break; | 
|  | case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: | 
|  | case GL_TRANSFORM_FEEDBACK_VARYINGS: | 
|  | case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: | 
|  | type = kES3TransformFeedbackVaryings; | 
|  | break; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | Program* info = GetProgramInfo(gl, program, type); | 
|  | if (!info) { | 
|  | return false; | 
|  | } | 
|  | return info->GetProgramiv(pname, params); | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetActiveUniformsiv( | 
|  | GLES2Implementation* gl, GLuint program, GLsizei count, | 
|  | const GLuint* indices, GLenum pname, GLint* params) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | ProgramInfoType type = kNone; | 
|  | switch (pname) { | 
|  | case GL_UNIFORM_SIZE: | 
|  | case GL_UNIFORM_TYPE: | 
|  | case GL_UNIFORM_NAME_LENGTH: | 
|  | type = kES2; | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_INDEX: | 
|  | case GL_UNIFORM_OFFSET: | 
|  | case GL_UNIFORM_ARRAY_STRIDE: | 
|  | case GL_UNIFORM_MATRIX_STRIDE: | 
|  | case GL_UNIFORM_IS_ROW_MAJOR: | 
|  | type = kES3Uniformsiv; | 
|  | break; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | Program* info = GetProgramInfo(gl, program, type); | 
|  | if (info) { | 
|  | return info->GetUniformsiv(count, indices, pname, params); | 
|  | } | 
|  | return gl->GetActiveUniformsivHelper(program, count, indices, pname, params); | 
|  | } | 
|  |  | 
|  | GLint ProgramInfoManager::GetAttribLocation( | 
|  | GLES2Implementation* gl, GLuint program, const char* name) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES2); | 
|  | if (info) { | 
|  | return info->GetAttribLocation(name); | 
|  | } | 
|  | } | 
|  | return gl->GetAttribLocationHelper(program, name); | 
|  | } | 
|  |  | 
|  | GLint ProgramInfoManager::GetUniformLocation( | 
|  | GLES2Implementation* gl, GLuint program, const char* name) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES2); | 
|  | if (info) { | 
|  | return info->GetUniformLocation(name); | 
|  | } | 
|  | } | 
|  | return gl->GetUniformLocationHelper(program, name); | 
|  | } | 
|  |  | 
|  | GLint ProgramInfoManager::GetFragDataIndex(GLES2Implementation* gl, | 
|  | GLuint program, | 
|  | const char* name) { | 
|  | // TODO(zmo): make FragData indexes part of the ProgramInfo that are | 
|  | // fetched from the service side.  See crbug.com/452104. | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kNone); | 
|  | if (info) { | 
|  | GLint possible_index = info->GetFragDataIndex(name); | 
|  | if (possible_index != -1) | 
|  | return possible_index; | 
|  | } | 
|  | } | 
|  | GLint index = gl->GetFragDataIndexEXTHelper(program, name); | 
|  | if (index != -1) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kNone); | 
|  | if (info) { | 
|  | info->CacheFragDataIndex(name, index); | 
|  | } | 
|  | } | 
|  | return index; | 
|  | } | 
|  |  | 
|  | GLint ProgramInfoManager::GetFragDataLocation( | 
|  | GLES2Implementation* gl, GLuint program, const char* name) { | 
|  | // TODO(zmo): make FragData locations part of the ProgramInfo that are | 
|  | // fetched altogether from the service side.  See crbug.com/452104. | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kNone); | 
|  | if (info) { | 
|  | GLint possible_loc = info->GetFragDataLocation(name); | 
|  | if (possible_loc != -1) | 
|  | return possible_loc; | 
|  | } | 
|  | } | 
|  | GLint loc = gl->GetFragDataLocationHelper(program, name); | 
|  | if (loc != -1) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kNone); | 
|  | if (info) { | 
|  | info->CacheFragDataLocation(name, loc); | 
|  | } | 
|  | } | 
|  | return loc; | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetActiveAttrib( | 
|  | GLES2Implementation* gl, | 
|  | GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, | 
|  | GLint* size, GLenum* type, char* name) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES2); | 
|  | if (info) { | 
|  | const Program::VertexAttrib* attrib_info = info->GetAttribInfo(index); | 
|  | if (attrib_info) { | 
|  | if (size) { | 
|  | *size = attrib_info->size; | 
|  | } | 
|  | if (type) { | 
|  | *type = attrib_info->type; | 
|  | } | 
|  | if (length || name) { | 
|  | GLsizei max_size = std::min( | 
|  | static_cast<size_t>(bufsize) - 1, | 
|  | std::max(static_cast<size_t>(0), attrib_info->name.size())); | 
|  | if (length) { | 
|  | *length = max_size; | 
|  | } | 
|  | if (name && bufsize > 0) { | 
|  | memcpy(name, attrib_info->name.c_str(), max_size); | 
|  | name[max_size] = '\0'; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return gl->GetActiveAttribHelper( | 
|  | program, index, bufsize, length, size, type, name); | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetActiveUniform( | 
|  | GLES2Implementation* gl, | 
|  | GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, | 
|  | GLint* size, GLenum* type, char* name) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES2); | 
|  | if (info) { | 
|  | const Program::UniformInfo* uniform_info = info->GetUniformInfo(index); | 
|  | if (uniform_info) { | 
|  | if (size) { | 
|  | *size = uniform_info->size; | 
|  | } | 
|  | if (type) { | 
|  | *type = uniform_info->type; | 
|  | } | 
|  | if (length || name) { | 
|  | GLsizei max_size = std::min( | 
|  | static_cast<size_t>(bufsize) - 1, | 
|  | std::max(static_cast<size_t>(0), uniform_info->name.size())); | 
|  | if (length) { | 
|  | *length = max_size; | 
|  | } | 
|  | if (name && bufsize > 0) { | 
|  | memcpy(name, uniform_info->name.c_str(), max_size); | 
|  | name[max_size] = '\0'; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return gl->GetActiveUniformHelper( | 
|  | program, index, bufsize, length, size, type, name); | 
|  | } | 
|  |  | 
|  | GLuint ProgramInfoManager::GetUniformBlockIndex( | 
|  | GLES2Implementation* gl, GLuint program, const char* name) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES3UniformBlocks); | 
|  | if (info) { | 
|  | return info->GetUniformBlockIndex(name); | 
|  | } | 
|  | } | 
|  | return gl->GetUniformBlockIndexHelper(program, name); | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetActiveUniformBlockName( | 
|  | GLES2Implementation* gl, GLuint program, GLuint index, | 
|  | GLsizei buf_size, GLsizei* length, char* name) { | 
|  | DCHECK_LE(0, buf_size); | 
|  | if (!name) { | 
|  | buf_size = 0; | 
|  | } | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES3UniformBlocks); | 
|  | if (info) { | 
|  | const Program::UniformBlock* uniform_block = info->GetUniformBlock(index); | 
|  | if (uniform_block) { | 
|  | if (buf_size == 0) { | 
|  | if (length) { | 
|  | *length = 0; | 
|  | } | 
|  | } else if (length || name) { | 
|  | GLsizei max_size = std::min( | 
|  | buf_size - 1, static_cast<GLsizei>(uniform_block->name.size())); | 
|  | if (length) { | 
|  | *length = max_size; | 
|  | } | 
|  | if (name) { | 
|  | memcpy(name, uniform_block->name.data(), max_size); | 
|  | name[max_size] = '\0'; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return gl->GetActiveUniformBlockNameHelper( | 
|  | program, index, buf_size, length, name); | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetActiveUniformBlockiv( | 
|  | GLES2Implementation* gl, GLuint program, GLuint index, | 
|  | GLenum pname, GLint* params) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES3UniformBlocks); | 
|  | if (info) { | 
|  | const Program::UniformBlock* uniform_block = info->GetUniformBlock(index); | 
|  | bool valid_pname; | 
|  | switch (pname) { | 
|  | case GL_UNIFORM_BLOCK_BINDING: | 
|  | case GL_UNIFORM_BLOCK_DATA_SIZE: | 
|  | case GL_UNIFORM_BLOCK_NAME_LENGTH: | 
|  | case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: | 
|  | case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: | 
|  | case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: | 
|  | case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: | 
|  | valid_pname = true; | 
|  | break; | 
|  | default: | 
|  | valid_pname = false; | 
|  | break; | 
|  | } | 
|  | if (uniform_block && valid_pname && params) { | 
|  | switch (pname) { | 
|  | case GL_UNIFORM_BLOCK_BINDING: | 
|  | *params = static_cast<GLint>(uniform_block->binding); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_DATA_SIZE: | 
|  | *params = static_cast<GLint>(uniform_block->data_size); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_NAME_LENGTH: | 
|  | *params = static_cast<GLint>(uniform_block->name.size()) + 1; | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: | 
|  | *params = static_cast<GLint>( | 
|  | uniform_block->active_uniform_indices.size()); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: | 
|  | for (size_t ii = 0; | 
|  | ii < uniform_block->active_uniform_indices.size(); ++ii) { | 
|  | params[ii] = static_cast<GLint>( | 
|  | uniform_block->active_uniform_indices[ii]); | 
|  | } | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: | 
|  | *params = static_cast<GLint>( | 
|  | uniform_block->referenced_by_vertex_shader); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: | 
|  | *params = static_cast<GLint>( | 
|  | uniform_block->referenced_by_fragment_shader); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return gl->GetActiveUniformBlockivHelper(program, index, pname, params); | 
|  | } | 
|  |  | 
|  | void ProgramInfoManager::UniformBlockBinding( | 
|  | GLES2Implementation* gl, GLuint program, GLuint index, GLuint binding) { | 
|  | GLuint max_bindings = | 
|  | static_cast<GLuint>(gl->capabilities().max_uniform_buffer_bindings); | 
|  | if (binding < max_bindings) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | // If UniformBlock info haven't been cached yet, skip updating the binding. | 
|  | Program* info = GetProgramInfo(gl, program, kNone); | 
|  | if (info) { | 
|  | info->UniformBlockBinding(index, binding); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetTransformFeedbackVarying( | 
|  | GLES2Implementation* gl, GLuint program, GLuint index, GLsizei bufsize, | 
|  | GLsizei* length, GLsizei* size, GLenum* type, char* name) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES3TransformFeedbackVaryings); | 
|  | if (info) { | 
|  | const Program::TransformFeedbackVarying* varying = | 
|  | info->GetTransformFeedbackVarying(index); | 
|  | if (varying) { | 
|  | if (size) { | 
|  | *size = varying->size; | 
|  | } | 
|  | if (type) { | 
|  | *type = varying->type; | 
|  | } | 
|  | if (length || name) { | 
|  | GLsizei max_size = std::min( | 
|  | bufsize - 1, static_cast<GLsizei>(varying->name.size())); | 
|  | if (length) { | 
|  | *length = static_cast<GLsizei>(max_size); | 
|  | } | 
|  | if (name && bufsize > 0) { | 
|  | memcpy(name, varying->name.c_str(), max_size); | 
|  | name[max_size] = '\0'; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return gl->GetTransformFeedbackVaryingHelper( | 
|  | program, index, bufsize, length, size, type, name); | 
|  | } | 
|  |  | 
|  | bool ProgramInfoManager::GetUniformIndices(GLES2Implementation* gl, | 
|  | GLuint program, GLsizei count, const char* const* names, GLuint* indices) { | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | Program* info = GetProgramInfo(gl, program, kES2); | 
|  | if (info) { | 
|  | DCHECK_LT(0, count); | 
|  | DCHECK(names && indices); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | indices[ii] = info->GetUniformIndex(names[ii]); | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return gl->GetUniformIndicesHelper(program, count, names, indices); | 
|  | } | 
|  |  | 
|  | }  // namespace gles2 | 
|  | }  // namespace gpu | 
|  |  |