| // 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 <stddef.h> |
| #include <stdint.h> |
| |
| #include "gpu/command_buffer/client/program_info_manager.h" |
| |
| namespace { |
| |
| template <typename T> |
| static T LocalGetAs(const std::vector<int8_t>& data, |
| uint32_t offset, |
| size_t size) { |
| const int8_t* p = data.data() + offset; |
| DCHECK_LE(offset + size, data.size()); |
| return static_cast<T>(static_cast<const void*>(p)); |
| } |
| |
| // Writes the strimg pointed by name and of maximum size buffsize. If length is |
| // !null, it receives the number of characters written (excluding the final \0). |
| // This is a helper function for GetActive*Helper functions that return names. |
| void FillNameAndLength(GLsizei bufsize, |
| GLsizei* length, |
| char* name, |
| const std::string& string) { |
| // Length of string (without final \0) that we will write to the |
| // buffer. |
| GLsizei max_length = 0; |
| if (name && (bufsize > 0)) { |
| DCHECK_LE(string.size(), static_cast<size_t>(INT_MAX)); |
| // Note: bufsize counts the terminating \0, but not string.size(). |
| max_length = std::min(bufsize - 1, static_cast<GLsizei>(string.size())); |
| memcpy(name, string.data(), max_length); |
| name[max_length] = '\0'; |
| } |
| if (length) { |
| *length = max_length; |
| } |
| } |
| |
| } // namespace |
| |
| 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() = default; |
| |
| ProgramInfoManager::Program::UniformInfo::UniformInfo( |
| GLsizei _size, GLenum _type, const std::string& _name) |
| : size(_size), |
| type(_type), |
| name(_name) { |
| is_array = (!name.empty() && name.back() == ']'); |
| DCHECK(!(size > 1 && !is_array)); |
| } |
| |
| ProgramInfoManager::Program::UniformInfo::UniformInfo( |
| const UniformInfo& other) = default; |
| |
| ProgramInfoManager::Program::UniformInfo::~UniformInfo() = default; |
| |
| ProgramInfoManager::Program::UniformES3::UniformES3() |
| : block_index(-1), |
| offset(-1), |
| array_stride(-1), |
| matrix_stride(-1), |
| is_row_major(0) { |
| } |
| |
| ProgramInfoManager::Program::UniformES3::~UniformES3() = default; |
| |
| ProgramInfoManager::Program::UniformBlock::UniformBlock() |
| : binding(0), |
| data_size(0), |
| referenced_by_vertex_shader(false), |
| referenced_by_fragment_shader(false) { |
| } |
| |
| ProgramInfoManager::Program::UniformBlock::UniformBlock( |
| const UniformBlock& other) = default; |
| |
| ProgramInfoManager::Program::UniformBlock::~UniformBlock() = default; |
| |
| ProgramInfoManager::Program::TransformFeedbackVarying:: |
| TransformFeedbackVarying() |
| : size(0), |
| type(0) { |
| } |
| |
| ProgramInfoManager::Program::TransformFeedbackVarying:: |
| ~TransformFeedbackVarying() = default; |
| |
| 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(const Program& other) = default; |
| |
| ProgramInfoManager::Program::~Program() = default; |
| |
| // 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] |
| : nullptr; |
| } |
| |
| const ProgramInfoManager::Program::UniformInfo* |
| ProgramInfoManager::Program::GetUniformInfo(GLint index) const { |
| return (static_cast<size_t>(index) < uniform_infos_.size()) |
| ? &uniform_infos_[index] |
| : nullptr; |
| } |
| |
| const ProgramInfoManager::Program::UniformBlock* |
| ProgramInfoManager::Program::GetUniformBlock(GLuint index) const { |
| return (index < uniform_blocks_.size()) ? &uniform_blocks_[index] : nullptr; |
| } |
| |
| 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 { |
| std::unordered_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] |
| : nullptr; |
| } |
| |
| 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; |
| } |
| if (num_uniforms != uniforms_es3_.size()) { |
| return false; |
| } |
| switch (pname) { |
| case GL_UNIFORM_BLOCK_INDEX: |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| params[ii] = uniforms_es3_[indices[ii]].block_index; |
| } |
| return true; |
| case GL_UNIFORM_OFFSET: |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| params[ii] = uniforms_es3_[indices[ii]].offset; |
| } |
| return true; |
| case GL_UNIFORM_ARRAY_STRIDE: |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| params[ii] = uniforms_es3_[indices[ii]].array_stride; |
| } |
| return true; |
| case GL_UNIFORM_MATRIX_STRIDE: |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| params[ii] = uniforms_es3_[indices[ii]].matrix_stride; |
| } |
| return true; |
| case GL_UNIFORM_IS_ROW_MAJOR: |
| 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_t>& 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_t ii = 0; ii < header->num_attribs; ++ii) { |
| const int32_t* location = LocalGetAs<const int32_t*>( |
| result, input->location_offset, sizeof(int32_t)); |
| 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_t ii = 0; ii < header->num_uniforms; ++ii) { |
| const int32_t* locations = LocalGetAs<const int32_t*>( |
| result, input->location_offset, sizeof(int32_t) * 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_t 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_t>(input - inputs)); |
| cached_es2_ = true; |
| } |
| |
| void ProgramInfoManager::Program::UpdateES3UniformBlocks( |
| const std::vector<int8_t>& 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_t>& 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_t>& 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() = default; |
| |
| ProgramInfoManager::~ProgramInfoManager() = default; |
| |
| 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 nullptr; |
| } |
| Program* info = &it->second; |
| if (info->IsCached(type)) |
| return info; |
| |
| std::vector<int8_t> 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 nullptr; |
| } |
| 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: |
| break; |
| } |
| if (type != kNone) { |
| 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; |
| } |
| FillNameAndLength(bufsize, length, name, attrib_info->name); |
| 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; |
| } |
| FillNameAndLength(bufsize, length, name, uniform_info->name); |
| 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); |
| { |
| base::AutoLock auto_lock(lock_); |
| Program* info = GetProgramInfo(gl, program, kES3UniformBlocks); |
| if (info) { |
| const Program::UniformBlock* uniform_block = info->GetUniformBlock(index); |
| if (uniform_block) { |
| FillNameAndLength(buf_size, length, name, uniform_block->name); |
| 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; |
| } |
| FillNameAndLength(bufsize, length, name, varying->name); |
| 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); |
| } |
| |
| bool ProgramInfoManager::GetProgramInterfaceiv( |
| GLES2Implementation* gl, GLuint program, GLenum program_interface, |
| GLenum pname, GLint* params) { |
| // TODO(jiajie.hu@intel.com): The info is not cached for now, so always |
| // fallback to the IPC path. |
| return false; |
| } |
| |
| GLuint ProgramInfoManager::GetProgramResourceIndex( |
| GLES2Implementation* gl, GLuint program, GLenum program_interface, |
| const char* name) { |
| // TODO(jiajie.hu@intel.com): The info is not cached for now, so always |
| // fallback to the IPC path. |
| return gl->GetProgramResourceIndexHelper(program, program_interface, name); |
| } |
| |
| bool ProgramInfoManager::GetProgramResourceName( |
| GLES2Implementation* gl, GLuint program, GLenum program_interface, |
| GLuint index, GLsizei bufsize, GLsizei* length, char* name) { |
| // TODO(jiajie.hu@intel.com): The info is not cached for now, so always |
| // fallback to the IPC path. |
| return gl->GetProgramResourceNameHelper( |
| program, program_interface, index, bufsize, length, name); |
| } |
| |
| bool ProgramInfoManager::GetProgramResourceiv( |
| GLES2Implementation* gl, GLuint program, GLenum program_interface, |
| GLuint index, GLsizei prop_count, const GLenum* props, GLsizei bufsize, |
| GLsizei* length, GLint* params) { |
| // TODO(jiajie.hu@intel.com): The info is not cached for now, so always |
| // fallback to the IPC path. |
| return gl->GetProgramResourceivHelper( |
| program, program_interface, index, prop_count, props, bufsize, length, |
| params); |
| } |
| |
| GLint ProgramInfoManager::GetProgramResourceLocation( |
| GLES2Implementation* gl, GLuint program, GLenum program_interface, |
| const char* name) { |
| // TODO(jiajie.hu@intel.com): The info is not cached for now, so always |
| // fallback to the IPC path. |
| return gl->GetProgramResourceLocationHelper(program, program_interface, name); |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |
| |