|  | // 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/service/program_manager.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <set> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/containers/hash_tables.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/numerics/safe_math.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/time/time.h" | 
|  | #include "gpu/command_buffer/common/gles2_cmd_format.h" | 
|  | #include "gpu/command_buffer/common/gles2_cmd_utils.h" | 
|  | #include "gpu/command_buffer/service/feature_info.h" | 
|  | #include "gpu/command_buffer/service/gles2_cmd_decoder.h" | 
|  | #include "gpu/command_buffer/service/gpu_preferences.h" | 
|  | #include "gpu/command_buffer/service/program_cache.h" | 
|  | #include "gpu/command_buffer/service/shader_manager.h" | 
|  | #include "third_party/re2/src/re2/re2.h" | 
|  | #include "ui/gl/gl_version_info.h" | 
|  |  | 
|  | using base::TimeDelta; | 
|  | using base::TimeTicks; | 
|  |  | 
|  | namespace gpu { | 
|  | namespace gles2 { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | int ShaderTypeToIndex(GLenum shader_type) { | 
|  | switch (shader_type) { | 
|  | case GL_VERTEX_SHADER: | 
|  | return 0; | 
|  | case GL_FRAGMENT_SHADER: | 
|  | return 1; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Given a name like "foo.bar[123].moo[456]" sets new_name to "foo.bar[123].moo" | 
|  | // and sets element_index to 456. returns false if element expression was not a | 
|  | // whole decimal number. For example: "foo[1b2]" | 
|  | bool GetUniformNameSansElement( | 
|  | const std::string& name, int* element_index, std::string* new_name) { | 
|  | DCHECK(element_index); | 
|  | DCHECK(new_name); | 
|  | if (name.size() < 3 || name.back() != ']') { | 
|  | *element_index = 0; | 
|  | *new_name = name; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Look for an array specification. | 
|  | size_t open_pos = name.find_last_of('['); | 
|  | if (open_pos == std::string::npos || | 
|  | open_pos >= name.size() - 2) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | base::CheckedNumeric<GLint> index = 0; | 
|  | size_t last = name.size() - 1; | 
|  | for (size_t pos = open_pos + 1; pos < last; ++pos) { | 
|  | int8_t digit = name[pos] - '0'; | 
|  | if (digit < 0 || digit > 9) { | 
|  | return false; | 
|  | } | 
|  | index = index * 10 + digit; | 
|  | } | 
|  | if (!index.IsValid()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *element_index = index.ValueOrDie(); | 
|  | *new_name = name.substr(0, open_pos); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool IsBuiltInFragmentVarying(const std::string& name) { | 
|  | // Built-in variables for fragment shaders. | 
|  | const char* kBuiltInVaryings[] = { | 
|  | "gl_FragCoord", | 
|  | "gl_FrontFacing", | 
|  | "gl_PointCoord" | 
|  | }; | 
|  | for (size_t ii = 0; ii < arraysize(kBuiltInVaryings); ++ii) { | 
|  | if (name == kBuiltInVaryings[ii]) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool IsBuiltInInvariant( | 
|  | const VaryingMap& varyings, const std::string& name) { | 
|  | VaryingMap::const_iterator hit = varyings.find(name); | 
|  | if (hit == varyings.end()) | 
|  | return false; | 
|  | return hit->second.isInvariant; | 
|  | } | 
|  |  | 
|  | uint32_t ComputeOffset(const void* start, const void* position) { | 
|  | return static_cast<const uint8_t*>(position) - | 
|  | static_cast<const uint8_t*>(start); | 
|  | } | 
|  |  | 
|  | ShaderVariableBaseType InputOutputTypeToBaseType(bool is_input, GLenum type) { | 
|  | switch (type) { | 
|  | case GL_INT: | 
|  | case GL_INT_VEC2: | 
|  | case GL_INT_VEC3: | 
|  | case GL_INT_VEC4: | 
|  | return SHADER_VARIABLE_INT; | 
|  | case GL_UNSIGNED_INT: | 
|  | case GL_UNSIGNED_INT_VEC2: | 
|  | case GL_UNSIGNED_INT_VEC3: | 
|  | case GL_UNSIGNED_INT_VEC4: | 
|  | return SHADER_VARIABLE_UINT; | 
|  | case GL_FLOAT: | 
|  | case GL_FLOAT_VEC2: | 
|  | case GL_FLOAT_VEC3: | 
|  | case GL_FLOAT_VEC4: | 
|  | return SHADER_VARIABLE_FLOAT; | 
|  | case GL_FLOAT_MAT2: | 
|  | case GL_FLOAT_MAT3: | 
|  | case GL_FLOAT_MAT4: | 
|  | case GL_FLOAT_MAT2x3: | 
|  | case GL_FLOAT_MAT3x2: | 
|  | case GL_FLOAT_MAT2x4: | 
|  | case GL_FLOAT_MAT4x2: | 
|  | case GL_FLOAT_MAT3x4: | 
|  | case GL_FLOAT_MAT4x3: | 
|  | DCHECK(is_input); | 
|  | return SHADER_VARIABLE_FLOAT; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return SHADER_VARIABLE_UNDEFINED_TYPE; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Scoped object which logs three UMA stats related to program cleanup when | 
|  | // destructed: | 
|  | // - GPU.DestroyProgramManagerPrograms.Elapsed - The amount of time spent | 
|  | //   destroying programs during ProgramManager::Destroy. | 
|  | // - GPU.DestroyProgramManagerPrograms.Programs - The number of progams | 
|  | //   destroyed during ProgramManager::Destroy. | 
|  | // - GPU.DestroyProgramManagerPrograms.ProgramsPerMs - The number of programs | 
|  | //   destroyed per millisecond during ProgramManager::Destroy. Only logged if | 
|  | //   both the number of programs and the elapsed time are greater than zero. | 
|  | class ProgramDeletionScopedUmaTimeAndRate { | 
|  | public: | 
|  | ProgramDeletionScopedUmaTimeAndRate(int32_t count) | 
|  | : count_(count), start_(base::TimeTicks::Now()) {} | 
|  |  | 
|  | ~ProgramDeletionScopedUmaTimeAndRate() { | 
|  | base::TimeDelta elapsed = base::TimeTicks::Now() - start_; | 
|  | UMA_HISTOGRAM_TIMES("GPU.DestroyProgramManagerPrograms.Elapsed", elapsed); | 
|  | UMA_HISTOGRAM_COUNTS("GPU.DestroyProgramManagerPrograms.Programs", count_); | 
|  |  | 
|  | double elapsed_ms = elapsed.InMillisecondsF(); | 
|  | if (count_ > 0 && elapsed_ms > 0) { | 
|  | double rate = static_cast<double>(count_) / elapsed_ms; | 
|  | UMA_HISTOGRAM_COUNTS("GPU.DestroyProgramManagerPrograms.ProgramsPerMs", | 
|  | base::saturated_cast<int32_t>(rate)); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | const int32_t count_; | 
|  | const base::TimeTicks start_; | 
|  | }; | 
|  |  | 
|  | }  // anonymous namespace. | 
|  |  | 
|  | Program::UniformInfo::UniformInfo() | 
|  | : size(0), | 
|  | type(GL_NONE), | 
|  | accepts_api_type(0), | 
|  | fake_location_base(0), | 
|  | is_array(false) {} | 
|  |  | 
|  | Program::UniformInfo::UniformInfo(const std::string& client_name, | 
|  | int client_location_base, | 
|  | GLenum _type, | 
|  | bool _is_array, | 
|  | const std::vector<GLint>& service_locations) | 
|  | : size(service_locations.size()), | 
|  | type(_type), | 
|  | accepts_api_type(0), | 
|  | fake_location_base(client_location_base), | 
|  | is_array(_is_array), | 
|  | name(client_name), | 
|  | element_locations(service_locations) { | 
|  | switch (type) { | 
|  | case GL_INT: | 
|  | accepts_api_type = kUniform1i; | 
|  | break; | 
|  | case GL_INT_VEC2: | 
|  | accepts_api_type = kUniform2i; | 
|  | break; | 
|  | case GL_INT_VEC3: | 
|  | accepts_api_type = kUniform3i; | 
|  | break; | 
|  | case GL_INT_VEC4: | 
|  | accepts_api_type = kUniform4i; | 
|  | break; | 
|  |  | 
|  | case GL_UNSIGNED_INT: | 
|  | accepts_api_type = kUniform1ui; | 
|  | break; | 
|  | case GL_UNSIGNED_INT_VEC2: | 
|  | accepts_api_type = kUniform2ui; | 
|  | break; | 
|  | case GL_UNSIGNED_INT_VEC3: | 
|  | accepts_api_type = kUniform3ui; | 
|  | break; | 
|  | case GL_UNSIGNED_INT_VEC4: | 
|  | accepts_api_type = kUniform4ui; | 
|  | break; | 
|  |  | 
|  | case GL_BOOL: | 
|  | accepts_api_type = kUniform1i | kUniform1ui | kUniform1f; | 
|  | break; | 
|  | case GL_BOOL_VEC2: | 
|  | accepts_api_type = kUniform2i | kUniform2ui | kUniform2f; | 
|  | break; | 
|  | case GL_BOOL_VEC3: | 
|  | accepts_api_type = kUniform3i | kUniform3ui | kUniform3f; | 
|  | break; | 
|  | case GL_BOOL_VEC4: | 
|  | accepts_api_type = kUniform4i | kUniform4ui | kUniform4f; | 
|  | break; | 
|  |  | 
|  | case GL_FLOAT: | 
|  | accepts_api_type = kUniform1f; | 
|  | break; | 
|  | case GL_FLOAT_VEC2: | 
|  | accepts_api_type = kUniform2f; | 
|  | break; | 
|  | case GL_FLOAT_VEC3: | 
|  | accepts_api_type = kUniform3f; | 
|  | break; | 
|  | case GL_FLOAT_VEC4: | 
|  | accepts_api_type = kUniform4f; | 
|  | break; | 
|  |  | 
|  | case GL_FLOAT_MAT2: | 
|  | accepts_api_type = kUniformMatrix2f; | 
|  | break; | 
|  | case GL_FLOAT_MAT3: | 
|  | accepts_api_type = kUniformMatrix3f; | 
|  | break; | 
|  | case GL_FLOAT_MAT4: | 
|  | accepts_api_type = kUniformMatrix4f; | 
|  | break; | 
|  |  | 
|  | case GL_FLOAT_MAT2x3: | 
|  | accepts_api_type = kUniformMatrix2x3f; | 
|  | break; | 
|  | case GL_FLOAT_MAT2x4: | 
|  | accepts_api_type = kUniformMatrix2x4f; | 
|  | break; | 
|  | case GL_FLOAT_MAT3x2: | 
|  | accepts_api_type = kUniformMatrix3x2f; | 
|  | break; | 
|  | case GL_FLOAT_MAT3x4: | 
|  | accepts_api_type = kUniformMatrix3x4f; | 
|  | break; | 
|  | case GL_FLOAT_MAT4x2: | 
|  | accepts_api_type = kUniformMatrix4x2f; | 
|  | break; | 
|  | case GL_FLOAT_MAT4x3: | 
|  | accepts_api_type = kUniformMatrix4x3f; | 
|  | break; | 
|  |  | 
|  | case GL_SAMPLER_2D: | 
|  | case GL_SAMPLER_2D_RECT_ARB: | 
|  | case GL_SAMPLER_CUBE: | 
|  | case GL_SAMPLER_3D_OES: | 
|  | case GL_SAMPLER_EXTERNAL_OES: | 
|  | case GL_SAMPLER_2D_ARRAY: | 
|  | case GL_SAMPLER_2D_SHADOW: | 
|  | case GL_SAMPLER_2D_ARRAY_SHADOW: | 
|  | case GL_SAMPLER_CUBE_SHADOW: | 
|  | case GL_INT_SAMPLER_2D: | 
|  | case GL_INT_SAMPLER_3D: | 
|  | case GL_INT_SAMPLER_CUBE: | 
|  | case GL_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_3D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_CUBE: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: | 
|  | accepts_api_type = kUniform1i; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | NOTREACHED() << "Unhandled UniformInfo type " << type; | 
|  | break; | 
|  | } | 
|  | DCHECK_LT(0, size); | 
|  | DCHECK(is_array || size == 1); | 
|  |  | 
|  | size_t num_texture_units = IsSampler() ? static_cast<size_t>(size) : 0u; | 
|  | texture_units.clear(); | 
|  | texture_units.resize(num_texture_units, 0); | 
|  | } | 
|  |  | 
|  | Program::UniformInfo::UniformInfo(const UniformInfo& other) = default; | 
|  |  | 
|  | Program::UniformInfo::~UniformInfo() {} | 
|  |  | 
|  | bool ProgramManager::HasBuiltInPrefix(const std::string& name) { | 
|  | return name.length() >= 3 && name[0] == 'g' && name[1] == 'l' && | 
|  | name[2] == '_'; | 
|  | } | 
|  |  | 
|  | Program::Program(ProgramManager* manager, GLuint service_id) | 
|  | : manager_(manager), | 
|  | use_count_(0), | 
|  | max_attrib_name_length_(0), | 
|  | max_uniform_name_length_(0), | 
|  | service_id_(service_id), | 
|  | deleted_(false), | 
|  | valid_(false), | 
|  | link_status_(false), | 
|  | uniforms_cleared_(false), | 
|  | transform_feedback_buffer_mode_(GL_NONE), | 
|  | fragment_output_type_mask_(0u), | 
|  | fragment_output_written_mask_(0u) { | 
|  | DCHECK(manager_); | 
|  | manager_->StartTracking(this); | 
|  | uint32_t packed_size = (manager_->max_vertex_attribs() + 15) / 16; | 
|  | vertex_input_base_type_mask_.resize(packed_size); | 
|  | vertex_input_active_mask_.resize(packed_size); | 
|  | ClearVertexInputMasks(); | 
|  | } | 
|  |  | 
|  | void Program::Reset() { | 
|  | valid_ = false; | 
|  | link_status_ = false; | 
|  | max_uniform_name_length_ = 0; | 
|  | max_attrib_name_length_ = 0; | 
|  | attrib_infos_.clear(); | 
|  | uniform_infos_.clear(); | 
|  | uniform_locations_.clear(); | 
|  | fragment_input_infos_.clear(); | 
|  | fragment_input_locations_.clear(); | 
|  | program_output_infos_.clear(); | 
|  | sampler_indices_.clear(); | 
|  | attrib_location_to_index_map_.clear(); | 
|  | fragment_output_type_mask_ = 0u; | 
|  | fragment_output_written_mask_ = 0u; | 
|  | ClearVertexInputMasks(); | 
|  | } | 
|  |  | 
|  | void Program::ClearVertexInputMasks() { | 
|  | for (uint32_t ii = 0; ii < vertex_input_base_type_mask_.size(); ++ii) { | 
|  | vertex_input_base_type_mask_[ii] = 0u; | 
|  | vertex_input_active_mask_[ii] = 0u; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::UpdateFragmentOutputBaseTypes() { | 
|  | fragment_output_type_mask_ = 0u; | 
|  | fragment_output_written_mask_ = 0u; | 
|  | Shader* fragment_shader = | 
|  | attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); | 
|  | DCHECK(fragment_shader); | 
|  | for (auto const& output : fragment_shader->output_variable_list()) { | 
|  | int location = output.location; | 
|  | DCHECK(location == -1 || | 
|  | (location >= 0 && | 
|  | location < static_cast<int>(manager_->max_draw_buffers()))); | 
|  | if (location == -1) | 
|  | location = 0; | 
|  | if (ProgramManager::HasBuiltInPrefix(output.name)) { | 
|  | if (output.name != "gl_FragColor" && output.name != "gl_FragData") | 
|  | continue; | 
|  | } | 
|  | int count = static_cast<int>(output.arraySize == 0 ? 1 : output.arraySize); | 
|  | // TODO(zmo): Handle the special case in ES2 where gl_FragColor could | 
|  | // be broadcasting to all draw buffers. | 
|  | DCHECK_LE(location + count, | 
|  | static_cast<int>(manager_->max_draw_buffers())); | 
|  | for (int ii = location; ii < location + count; ++ii) { | 
|  | // TODO(zmo): This does not work with glBindFragDataLocationIndexed. | 
|  | // crbug.com/628010 | 
|  | // For example: | 
|  | //    glBindFragDataLocationIndexed(program, loc, 0, "FragData0"); | 
|  | //    glBindFragDataLocationIndexed(program, loc, 1, "FragData1"); | 
|  | // The program links OK, but both calling glGetFragDataLocation on both | 
|  | // "FragData0" and "FragData1" returns 0. | 
|  | int shift_bits = ii * 2; | 
|  | fragment_output_written_mask_ |= 0x3 << shift_bits; | 
|  | fragment_output_type_mask_ |= | 
|  | InputOutputTypeToBaseType(false, output.type) << shift_bits; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::UpdateVertexInputBaseTypes() { | 
|  | ClearVertexInputMasks(); | 
|  | DCHECK_LE(attrib_infos_.size(), manager_->max_vertex_attribs()); | 
|  | for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) { | 
|  | const VertexAttrib& input = attrib_infos_[ii]; | 
|  | if (ProgramManager::HasBuiltInPrefix(input.name)) { | 
|  | continue; | 
|  | } | 
|  | int shift_bits = (input.location % 16) * 2; | 
|  | vertex_input_active_mask_[ii / 16] |= 0x3 << shift_bits; | 
|  | vertex_input_base_type_mask_[ii / 16] |= | 
|  | InputOutputTypeToBaseType(true, input.type) << shift_bits; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::UpdateUniformBlockSizeInfo() { | 
|  | if (feature_info().IsWebGL1OrES2Context()) { | 
|  | // Uniform blocks do not exist in ES2. | 
|  | return; | 
|  | } | 
|  |  | 
|  | uniform_block_size_info_.clear(); | 
|  |  | 
|  | GLint num_uniform_blocks = 0; | 
|  | glGetProgramiv(service_id_, GL_ACTIVE_UNIFORM_BLOCKS, &num_uniform_blocks); | 
|  | uniform_block_size_info_.resize(num_uniform_blocks); | 
|  | for (GLint ii = 0; ii < num_uniform_blocks; ++ii) { | 
|  | GLint binding = 0; | 
|  | glGetActiveUniformBlockiv( | 
|  | service_id_, ii, GL_UNIFORM_BLOCK_BINDING, &binding); | 
|  | uniform_block_size_info_[ii].binding = static_cast<GLuint>(binding); | 
|  |  | 
|  | GLint size = 0; | 
|  | glGetActiveUniformBlockiv( | 
|  | service_id_, ii, GL_UNIFORM_BLOCK_DATA_SIZE, &size); | 
|  | uniform_block_size_info_[ii].data_size = static_cast<GLuint>(size); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::SetUniformBlockBinding(GLuint index, GLuint binding) { | 
|  | DCHECK_GT(uniform_block_size_info_.size(), index); | 
|  | uniform_block_size_info_[index].binding = binding; | 
|  | } | 
|  |  | 
|  | std::string Program::ProcessLogInfo( | 
|  | const std::string& log) { | 
|  | std::string output; | 
|  | re2::StringPiece input(log); | 
|  | std::string prior_log; | 
|  | std::string hashed_name; | 
|  | while (RE2::Consume(&input, | 
|  | "(.*?)(webgl_[0123456789abcdefABCDEF]+)", | 
|  | &prior_log, | 
|  | &hashed_name)) { | 
|  | output += prior_log; | 
|  |  | 
|  | const std::string* original_name = | 
|  | GetOriginalNameFromHashedName(hashed_name); | 
|  | if (original_name) | 
|  | output += *original_name; | 
|  | else | 
|  | output += hashed_name; | 
|  | } | 
|  |  | 
|  | return output + input.as_string(); | 
|  | } | 
|  |  | 
|  | void Program::UpdateLogInfo() { | 
|  | GLint max_len = 0; | 
|  | glGetProgramiv(service_id_, GL_INFO_LOG_LENGTH, &max_len); | 
|  | if (max_len == 0) { | 
|  | set_log_info(nullptr); | 
|  | return; | 
|  | } | 
|  | std::unique_ptr<char[]> temp(new char[max_len]); | 
|  | GLint len = 0; | 
|  | glGetProgramInfoLog(service_id_, max_len, &len, temp.get()); | 
|  | DCHECK(max_len == 0 || len < max_len); | 
|  | DCHECK(len == 0 || temp[len] == '\0'); | 
|  | std::string log(temp.get(), len); | 
|  | log = ProcessLogInfo(log); | 
|  | set_log_info(log.empty() ? nullptr : log.c_str()); | 
|  | } | 
|  |  | 
|  | void Program::ClearUniforms(std::vector<uint8_t>* zero_buffer) { | 
|  | DCHECK(zero_buffer); | 
|  | if (uniforms_cleared_) { | 
|  | return; | 
|  | } | 
|  | uniforms_cleared_ = true; | 
|  | for (const UniformInfo& uniform_info : uniform_infos_) { | 
|  | GLint location = uniform_info.element_locations[0]; | 
|  | GLsizei size = uniform_info.size; | 
|  | uint32_t unit_size = | 
|  | GLES2Util::GetElementCountForUniformType(uniform_info.type) * | 
|  | GLES2Util::GetElementSizeForUniformType(uniform_info.type); | 
|  | DCHECK_LT(0u, unit_size); | 
|  | uint32_t size_needed = size * unit_size; | 
|  | if (size_needed > zero_buffer->size()) { | 
|  | zero_buffer->resize(size_needed, 0u); | 
|  | } | 
|  | const void* zero = &(*zero_buffer)[0]; | 
|  | switch (uniform_info.type) { | 
|  | case GL_FLOAT: | 
|  | glUniform1fv(location, size, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_VEC2: | 
|  | glUniform2fv(location, size, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_VEC3: | 
|  | glUniform3fv(location, size, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_VEC4: | 
|  | glUniform4fv(location, size, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_INT: | 
|  | case GL_BOOL: | 
|  | case GL_SAMPLER_2D: | 
|  | case GL_SAMPLER_CUBE: | 
|  | case GL_SAMPLER_EXTERNAL_OES:  // extension. | 
|  | case GL_SAMPLER_2D_RECT_ARB:  // extension. | 
|  | glUniform1iv(location, size, reinterpret_cast<const GLint*>(zero)); | 
|  | break; | 
|  | case GL_INT_VEC2: | 
|  | case GL_BOOL_VEC2: | 
|  | glUniform2iv(location, size, reinterpret_cast<const GLint*>(zero)); | 
|  | break; | 
|  | case GL_INT_VEC3: | 
|  | case GL_BOOL_VEC3: | 
|  | glUniform3iv(location, size, reinterpret_cast<const GLint*>(zero)); | 
|  | break; | 
|  | case GL_INT_VEC4: | 
|  | case GL_BOOL_VEC4: | 
|  | glUniform4iv(location, size, reinterpret_cast<const GLint*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT2: | 
|  | glUniformMatrix2fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT3: | 
|  | glUniformMatrix3fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT4: | 
|  | glUniformMatrix4fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  |  | 
|  | // ES3 types. | 
|  | case GL_UNSIGNED_INT: | 
|  | glUniform1uiv(location, size, reinterpret_cast<const GLuint*>(zero)); | 
|  | break; | 
|  | case GL_SAMPLER_3D: | 
|  | case GL_SAMPLER_2D_SHADOW: | 
|  | case GL_SAMPLER_2D_ARRAY: | 
|  | case GL_SAMPLER_2D_ARRAY_SHADOW: | 
|  | case GL_SAMPLER_CUBE_SHADOW: | 
|  | case GL_INT_SAMPLER_2D: | 
|  | case GL_INT_SAMPLER_3D: | 
|  | case GL_INT_SAMPLER_CUBE: | 
|  | case GL_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_3D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_CUBE: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: | 
|  | glUniform1iv(location, size, reinterpret_cast<const GLint*>(zero)); | 
|  | break; | 
|  | case GL_UNSIGNED_INT_VEC2: | 
|  | glUniform2uiv(location, size, reinterpret_cast<const GLuint*>(zero)); | 
|  | break; | 
|  | case GL_UNSIGNED_INT_VEC3: | 
|  | glUniform3uiv(location, size, reinterpret_cast<const GLuint*>(zero)); | 
|  | break; | 
|  | case GL_UNSIGNED_INT_VEC4: | 
|  | glUniform4uiv(location, size, reinterpret_cast<const GLuint*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT2x3: | 
|  | glUniformMatrix2x3fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT3x2: | 
|  | glUniformMatrix3x2fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT2x4: | 
|  | glUniformMatrix2x4fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT4x2: | 
|  | glUniformMatrix4x2fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT3x4: | 
|  | glUniformMatrix3x4fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  | case GL_FLOAT_MAT4x3: | 
|  | glUniformMatrix4x3fv( | 
|  | location, size, false, reinterpret_cast<const GLfloat*>(zero)); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::Update() { | 
|  | Reset(); | 
|  | UpdateLogInfo(); | 
|  | link_status_ = true; | 
|  | uniforms_cleared_ = false; | 
|  | GLint num_attribs = 0; | 
|  | GLint max_len = 0; | 
|  | GLint max_location = -1; | 
|  | glGetProgramiv(service_id_, GL_ACTIVE_ATTRIBUTES, &num_attribs); | 
|  | glGetProgramiv(service_id_, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); | 
|  | // TODO(gman): Should we check for error? | 
|  | std::unique_ptr<char[]> name_buffer(new char[max_len]); | 
|  | for (GLint ii = 0; ii < num_attribs; ++ii) { | 
|  | GLsizei length = 0; | 
|  | GLsizei size = 0; | 
|  | GLenum type = 0; | 
|  | glGetActiveAttrib( | 
|  | service_id_, ii, max_len, &length, &size, &type, name_buffer.get()); | 
|  | DCHECK(max_len == 0 || length < max_len); | 
|  | DCHECK(length == 0 || name_buffer[length] == '\0'); | 
|  | std::string original_name; | 
|  | GetVertexAttribData(name_buffer.get(), &original_name, &type); | 
|  | // TODO(gman): Should we check for error? | 
|  | GLint location = glGetAttribLocation(service_id_, name_buffer.get()); | 
|  | if (location > max_location) { | 
|  | max_location = location; | 
|  | } | 
|  | attrib_infos_.push_back(VertexAttrib(1, type, original_name, location)); | 
|  | max_attrib_name_length_ = std::max( | 
|  | max_attrib_name_length_, static_cast<GLsizei>(original_name.size())); | 
|  | } | 
|  |  | 
|  | // Create attrib location to index map. | 
|  | attrib_location_to_index_map_.resize(max_location + 1); | 
|  | for (GLint ii = 0; ii <= max_location; ++ii) { | 
|  | attrib_location_to_index_map_[ii] = -1; | 
|  | } | 
|  | for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) { | 
|  | const VertexAttrib& info = attrib_infos_[ii]; | 
|  | if (info.location >= 0 && info.location <= max_location) { | 
|  | attrib_location_to_index_map_[info.location] = ii; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (manager_->gpu_preferences_.enable_gpu_service_logging_gpu) { | 
|  | DVLOG(1) << "----: attribs for service_id: " << service_id(); | 
|  | for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) { | 
|  | const VertexAttrib& info = attrib_infos_[ii]; | 
|  | DVLOG(1) << ii << ": loc = " << info.location | 
|  | << ", size = " << info.size | 
|  | << ", type = " << GLES2Util::GetStringEnum(info.type) | 
|  | << ", name = " << info.name; | 
|  | } | 
|  | } | 
|  |  | 
|  | UpdateUniforms(); | 
|  |  | 
|  | if (manager_->gpu_preferences_.enable_gpu_service_logging_gpu) { | 
|  | DVLOG(1) << "----: uniforms for service_id: " << service_id(); | 
|  | size_t ii = 0; | 
|  | for (const UniformInfo& info : uniform_infos_) { | 
|  | DVLOG(1) << ii++ << ": loc = " << info.element_locations[0] | 
|  | << ", size = " << info.size | 
|  | << ", type = " << GLES2Util::GetStringEnum(info.type) | 
|  | << ", name = " << info.name; | 
|  | } | 
|  | } | 
|  |  | 
|  | UpdateFragmentInputs(); | 
|  | UpdateProgramOutputs(); | 
|  | UpdateFragmentOutputBaseTypes(); | 
|  | UpdateVertexInputBaseTypes(); | 
|  | UpdateUniformBlockSizeInfo(); | 
|  |  | 
|  | valid_ = true; | 
|  | } | 
|  |  | 
|  | void Program::UpdateUniforms() { | 
|  | // Reserve each client-bound uniform location. This way unbound uniforms will | 
|  | // not be allocated to locations that user expects bound uniforms to be, even | 
|  | // if the expected uniforms are optimized away by the driver. | 
|  | for (const auto& binding : bind_uniform_location_map_) { | 
|  | if (binding.second < 0) | 
|  | continue; | 
|  | size_t client_location = static_cast<size_t>(binding.second); | 
|  | if (uniform_locations_.size() <= client_location) | 
|  | uniform_locations_.resize(client_location + 1); | 
|  | uniform_locations_[client_location].SetInactive(); | 
|  | } | 
|  |  | 
|  | GLint num_uniforms = 0; | 
|  | glGetProgramiv(service_id_, GL_ACTIVE_UNIFORMS, &num_uniforms); | 
|  | if (num_uniforms <= 0) | 
|  | return; | 
|  |  | 
|  | uniform_infos_.resize(num_uniforms); | 
|  |  | 
|  | GLint name_buffer_length = 0; | 
|  | glGetProgramiv(service_id_, GL_ACTIVE_UNIFORM_MAX_LENGTH, | 
|  | &name_buffer_length); | 
|  | DCHECK(name_buffer_length > 0); | 
|  | std::unique_ptr<char[]> name_buffer(new char[name_buffer_length]); | 
|  |  | 
|  | size_t unused_client_location_cursor = 0; | 
|  |  | 
|  | for (GLint uniform_index = 0; uniform_index < num_uniforms; ++uniform_index) { | 
|  | GLsizei name_length = 0; | 
|  | GLsizei size = 0; | 
|  | GLenum type = GL_NONE; | 
|  | glGetActiveUniform(service_id_, uniform_index, name_buffer_length, | 
|  | &name_length, &size, &type, name_buffer.get()); | 
|  | DCHECK(name_length < name_buffer_length); | 
|  | DCHECK(name_length == 0 || name_buffer[name_length] == '\0'); | 
|  | std::string service_name(name_buffer.get(), name_length); | 
|  |  | 
|  | GLint service_location = -1; | 
|  | // Force builtin uniforms (gl_DepthRange) to have invalid location. | 
|  | if (!ProgramManager::HasBuiltInPrefix(service_name)) { | 
|  | service_location = | 
|  | glGetUniformLocation(service_id_, service_name.c_str()); | 
|  | } | 
|  |  | 
|  | // Determine the client name of the uniform and whether it is an array | 
|  | // or not. | 
|  | bool is_array = false; | 
|  | std::string client_name; | 
|  | for (size_t i = 0; i < kMaxAttachedShaders && client_name.empty(); ++i) { | 
|  | const auto& shader = attached_shaders_[i]; | 
|  | if (!shader) | 
|  | continue; | 
|  | const sh::ShaderVariable* info = nullptr; | 
|  | const sh::Uniform* uniform = shader->GetUniformInfo(service_name); | 
|  | if (uniform && | 
|  | uniform->findInfoByMappedName(service_name, &info, &client_name)) { | 
|  | DCHECK(!client_name.empty()); | 
|  | is_array = info->arraySize > 0; | 
|  | type = info->type; | 
|  | size = std::max(1u, info->arraySize); | 
|  | } else { | 
|  | const InterfaceBlockMap& interface_block_map = | 
|  | shader->interface_block_map(); | 
|  | for (const auto& key_value : interface_block_map) { | 
|  | const sh::InterfaceBlock& block = key_value.second; | 
|  | bool find = false; | 
|  | if (block.instanceName.empty()) { | 
|  | for (const auto& value : block.fields) { | 
|  | if (value.findInfoByMappedName(service_name, &info, | 
|  | &client_name)) { | 
|  | find = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | size_t pos = service_name.find_first_of('.'); | 
|  | std::string top_variable_name = service_name.substr(0, pos); | 
|  | if (block.mappedName == top_variable_name) { | 
|  | DCHECK(pos != std::string::npos); | 
|  | for (const auto& field : block.fields) { | 
|  | if (field.findInfoByMappedName(service_name.substr( | 
|  | pos + 1), &info, &client_name)) { | 
|  | find = true; | 
|  | client_name = block.name + "." + client_name; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (find) { | 
|  | DCHECK(!client_name.empty()); | 
|  | is_array = info->arraySize > 0; | 
|  | type = info->type; | 
|  | size = std::max(1u, info->arraySize); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (client_name.empty()) { | 
|  | // This happens only in cases where we do not have ANGLE or run unit tests | 
|  | // (or ANGLE has a severe bug). | 
|  | client_name = service_name; | 
|  | GLSLArrayName parsed_service_name(service_name); | 
|  | is_array = size > 1 || parsed_service_name.IsArrayName(); | 
|  | } | 
|  |  | 
|  | std::string service_base_name = service_name; | 
|  | std::string client_base_name = client_name; | 
|  | if (is_array) { | 
|  | // Some drivers incorrectly return an uniform name of size-1 array without | 
|  | // "[0]". In this case, we correct the service name by appending "[0]" to | 
|  | // it. | 
|  | GLSLArrayName parsed_service_name(service_name); | 
|  | if (parsed_service_name.IsArrayName()) { | 
|  | service_base_name = parsed_service_name.base_name(); | 
|  | GLSLArrayName parsed_client_name(client_name); | 
|  | client_base_name = parsed_client_name.base_name(); | 
|  | } else { | 
|  | service_name += "[0]"; | 
|  | client_name += "[0]"; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Assign a location for the uniform: use either client-bound | 
|  | // location or automatically assigned to an unused location. | 
|  | size_t client_location_base = 0; | 
|  | LocationMap::const_iterator it = | 
|  | bind_uniform_location_map_.find(client_base_name); | 
|  | if (it != bind_uniform_location_map_.end()) { | 
|  | client_location_base = it->second; | 
|  | } else { | 
|  | while (unused_client_location_cursor < uniform_locations_.size() && | 
|  | !uniform_locations_[unused_client_location_cursor].IsUnused()) | 
|  | unused_client_location_cursor++; | 
|  | if (unused_client_location_cursor == uniform_locations_.size()) | 
|  | uniform_locations_.resize(unused_client_location_cursor + 1); | 
|  | client_location_base = unused_client_location_cursor; | 
|  | unused_client_location_cursor++; | 
|  | } | 
|  |  | 
|  | // Populate the uniform list entry. | 
|  | std::vector<GLint> service_locations; | 
|  | service_locations.resize(size); | 
|  | service_locations[0] = service_location; | 
|  |  | 
|  | if (size > 1) { | 
|  | for (GLsizei ii = 1; ii < size; ++ii) { | 
|  | std::string element_name(service_base_name + "[" + | 
|  | base::IntToString(ii) + "]"); | 
|  | service_locations[ii] = | 
|  | glGetUniformLocation(service_id_, element_name.c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | UniformInfo& info = uniform_infos_[uniform_index]; | 
|  | info = UniformInfo(client_name, client_location_base, type, is_array, | 
|  | service_locations); | 
|  | if (info.IsSampler()) { | 
|  | sampler_indices_.push_back(uniform_index); | 
|  | } | 
|  |  | 
|  | // Populate the uniform location list entry. | 
|  | // Before linking, we already validated that no two statically used uniforms | 
|  | // are bound to the same location. | 
|  | DCHECK(!uniform_locations_[client_location_base].IsActive()); | 
|  | uniform_locations_[client_location_base].SetActive(&info); | 
|  |  | 
|  | max_uniform_name_length_ = std::max(max_uniform_name_length_, | 
|  | static_cast<GLsizei>(info.name.size())); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::UpdateFragmentInputs() { | 
|  | if (!feature_info().feature_flags().chromium_path_rendering) | 
|  | return; | 
|  | for (const auto& binding : bind_fragment_input_location_map_) { | 
|  | if (binding.second < 0) | 
|  | continue; | 
|  | size_t client_location = static_cast<size_t>(binding.second); | 
|  | if (fragment_input_locations_.size() <= client_location) | 
|  | fragment_input_locations_.resize(client_location + 1); | 
|  | fragment_input_locations_[client_location].SetInactive(); | 
|  | } | 
|  |  | 
|  | GLint num_fragment_inputs = 0; | 
|  | glGetProgramInterfaceiv(service_id_, GL_FRAGMENT_INPUT_NV, | 
|  | GL_ACTIVE_RESOURCES, &num_fragment_inputs); | 
|  | if (num_fragment_inputs <= 0) | 
|  | return; | 
|  |  | 
|  | GLint max_len = 0; | 
|  | glGetProgramInterfaceiv(service_id_, GL_FRAGMENT_INPUT_NV, GL_MAX_NAME_LENGTH, | 
|  | &max_len); | 
|  | DCHECK(max_len > 0); | 
|  |  | 
|  | std::unique_ptr<char[]> name_buffer(new char[max_len]); | 
|  |  | 
|  | Shader* fragment_shader = | 
|  | attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); | 
|  |  | 
|  | const GLenum kQueryProperties[] = {GL_LOCATION, GL_TYPE, GL_ARRAY_SIZE}; | 
|  |  | 
|  | std::vector<size_t> client_location_indices; | 
|  | for (GLint ii = 0; ii < num_fragment_inputs; ++ii) { | 
|  | GLsizei name_length = 0; | 
|  | glGetProgramResourceName(service_id_, GL_FRAGMENT_INPUT_NV, ii, max_len, | 
|  | &name_length, name_buffer.get()); | 
|  | DCHECK(name_length < max_len); | 
|  | DCHECK(name_length == 0 || name_buffer[name_length] == '\0'); | 
|  | // A fragment shader can have gl_FragCoord, gl_FrontFacing or gl_PointCoord | 
|  | // built-ins as its input, as well as custom varyings. We are interested in | 
|  | // custom varyings, client is allowed to bind only them. | 
|  | std::string service_name(name_buffer.get(), name_length); | 
|  | if (ProgramManager::HasBuiltInPrefix(service_name)) | 
|  | continue; | 
|  | // Unlike when binding uniforms, we expect the driver to give correct | 
|  | // names: "name" for simple variable, "name[0]" for an array. | 
|  | GLsizei query_length = 0; | 
|  | GLint query_results[arraysize(kQueryProperties)] = { | 
|  | 0, | 
|  | }; | 
|  | glGetProgramResourceiv(service_id_, GL_FRAGMENT_INPUT_NV, ii, | 
|  | arraysize(kQueryProperties), kQueryProperties, | 
|  | arraysize(query_results), &query_length, | 
|  | query_results); | 
|  | DCHECK(query_length == arraysize(kQueryProperties)); | 
|  |  | 
|  | GLenum type = static_cast<GLenum>(query_results[1]); | 
|  | GLsizei size = static_cast<GLsizei>(query_results[2]); | 
|  | std::string client_name; | 
|  |  | 
|  | const sh::Varying* varying = fragment_shader->GetVaryingInfo(service_name); | 
|  | const sh::ShaderVariable* info = nullptr; | 
|  | if (varying && | 
|  | varying->findInfoByMappedName(service_name, &info, &client_name)) { | 
|  | type = info->type; | 
|  | size = std::max(1u, info->arraySize); | 
|  | } else { | 
|  | // Should only happen if there are major bugs in the driver, ANGLE or if | 
|  | // the shader translator is disabled. | 
|  | DCHECK(feature_info().disable_shader_translator()); | 
|  | client_name = service_name; | 
|  | if (size <= 0) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | auto it = bind_fragment_input_location_map_.find(client_name); | 
|  | if (it != bind_fragment_input_location_map_.end() && it->second >= 0 && | 
|  | query_results[0] >= 0) { | 
|  | size_t client_location = static_cast<size_t>(it->second); | 
|  | GLuint service_location = static_cast<GLuint>(query_results[0]); | 
|  | fragment_input_infos_.push_back( | 
|  | FragmentInputInfo(type, service_location)); | 
|  | client_location_indices.push_back(client_location); | 
|  | } | 
|  |  | 
|  | if (size <= 1) | 
|  | continue; | 
|  | GLSLArrayName parsed_client_name(client_name); | 
|  | GLSLArrayName parsed_service_name(service_name); | 
|  | if (!parsed_client_name.IsArrayName() || | 
|  | parsed_client_name.element_index() != 0 || | 
|  | !parsed_service_name.IsArrayName() || | 
|  | parsed_service_name.element_index() != 0) { | 
|  | NOTREACHED() << "GLSL array variable names should end with \"[0]\". " | 
|  | "Likely driver or ANGLE error."; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | for (GLsizei jj = 1; jj < size; ++jj) { | 
|  | std::string array_spec(std::string("[") + base::IntToString(jj) + "]"); | 
|  | std::string client_element_name = | 
|  | parsed_client_name.base_name() + array_spec; | 
|  |  | 
|  | auto it = bind_fragment_input_location_map_.find(client_element_name); | 
|  | if (it != bind_fragment_input_location_map_.end() && it->second >= 0) { | 
|  | size_t client_location = static_cast<size_t>(it->second); | 
|  | std::string service_element_name = | 
|  | parsed_service_name.base_name() + array_spec; | 
|  | GLint service_location = glGetProgramResourceLocation( | 
|  | service_id_, GL_FRAGMENT_INPUT_NV, service_element_name.c_str()); | 
|  | if (service_location >= 0) { | 
|  | fragment_input_infos_.push_back( | 
|  | FragmentInputInfo(type, static_cast<GLuint>(service_location))); | 
|  | client_location_indices.push_back(client_location); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | for (size_t i = 0; i < client_location_indices.size(); ++i) { | 
|  | size_t client_location = client_location_indices[i]; | 
|  | // Before linking, we already validated that no two statically used fragment | 
|  | // inputs are bound to the same location. | 
|  | DCHECK(!fragment_input_locations_[client_location].IsActive()); | 
|  | fragment_input_locations_[client_location].SetActive( | 
|  | &fragment_input_infos_[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::UpdateProgramOutputs() { | 
|  | if (!feature_info().gl_version_info().is_es3_capable || | 
|  | feature_info().disable_shader_translator()) | 
|  | return; | 
|  |  | 
|  | Shader* fragment_shader = | 
|  | attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); | 
|  |  | 
|  | for (auto const& output_var : fragment_shader->output_variable_list()) { | 
|  | const std::string& service_name = output_var.mappedName; | 
|  | // A fragment shader can have gl_FragColor, gl_SecondaryFragColor, etc | 
|  | // built-ins as its output, as well as custom varyings. We are interested | 
|  | // only in custom varyings, client is allowed to bind only them. | 
|  | if (ProgramManager::HasBuiltInPrefix(service_name)) | 
|  | continue; | 
|  |  | 
|  | std::string client_name = output_var.name; | 
|  | if (output_var.arraySize == 0) { | 
|  | GLint color_name = | 
|  | glGetFragDataLocation(service_id_, service_name.c_str()); | 
|  | if (color_name < 0) | 
|  | continue; | 
|  | GLint index = 0; | 
|  | if (feature_info().feature_flags().ext_blend_func_extended) | 
|  | index = glGetFragDataIndex(service_id_, service_name.c_str()); | 
|  | if (index < 0) | 
|  | continue; | 
|  | program_output_infos_.push_back( | 
|  | ProgramOutputInfo(color_name, index, client_name)); | 
|  | } else if (feature_info().workarounds().get_frag_data_info_bug) { | 
|  | DCHECK(!feature_info().feature_flags().ext_blend_func_extended); | 
|  | GLint color_name = | 
|  | glGetFragDataLocation(service_id_, service_name.c_str()); | 
|  | if (color_name >= 0) { | 
|  | GLint index = 0; | 
|  | for (size_t ii = 0; ii < output_var.arraySize; ++ii) { | 
|  | std::string array_spec( | 
|  | std::string("[") + base::IntToString(ii) + "]"); | 
|  | program_output_infos_.push_back(ProgramOutputInfo( | 
|  | color_name + ii, index, client_name + array_spec)); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | for (size_t ii = 0; ii < output_var.arraySize; ++ii) { | 
|  | std::string array_spec(std::string("[") + base::IntToString(ii) + "]"); | 
|  | std::string service_element_name(service_name + array_spec); | 
|  | GLint color_name = | 
|  | glGetFragDataLocation(service_id_, service_element_name.c_str()); | 
|  | if (color_name < 0) | 
|  | continue; | 
|  | GLint index = 0; | 
|  | if (feature_info().feature_flags().ext_blend_func_extended) | 
|  | index = glGetFragDataIndex(service_id_, service_element_name.c_str()); | 
|  | if (index < 0) | 
|  | continue; | 
|  | program_output_infos_.push_back( | 
|  | ProgramOutputInfo(color_name, index, client_name + array_spec)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::ExecuteBindAttribLocationCalls() { | 
|  | for (const auto& key_value : bind_attrib_location_map_) { | 
|  | const std::string* mapped_name = GetAttribMappedName(key_value.first); | 
|  | if (mapped_name) | 
|  | glBindAttribLocation(service_id_, key_value.second, mapped_name->c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Program::ExecuteTransformFeedbackVaryingsCall() { | 
|  | if (!transform_feedback_varyings_.empty()) { | 
|  | Shader* vertex_shader = attached_shaders_[0].get(); | 
|  | if (!vertex_shader) { | 
|  | set_log_info("TransformFeedbackVaryings: missing vertex shader"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::vector<const char*> mapped_names; | 
|  | mapped_names.reserve(transform_feedback_varyings_.size()); | 
|  | for (StringVector::const_iterator it = | 
|  | transform_feedback_varyings_.begin(); | 
|  | it != transform_feedback_varyings_.end(); ++it) { | 
|  | const std::string& orig = *it; | 
|  | const std::string* mapped = vertex_shader->GetVaryingMappedName(orig); | 
|  | if (!mapped) { | 
|  | std::string log = "TransformFeedbackVaryings: no varying named " + orig; | 
|  | set_log_info(log.c_str()); | 
|  | return false; | 
|  | } | 
|  | mapped_names.push_back(mapped->c_str()); | 
|  | } | 
|  | glTransformFeedbackVaryings(service_id_, | 
|  | mapped_names.size(), | 
|  | &mapped_names.front(), | 
|  | transform_feedback_buffer_mode_); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Program::ExecuteProgramOutputBindCalls() { | 
|  | if (feature_info().disable_shader_translator()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | Shader* fragment_shader = | 
|  | attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); | 
|  | DCHECK(fragment_shader && fragment_shader->valid()); | 
|  |  | 
|  | if (fragment_shader->shader_version() != 100) { | 
|  | // ES SL 1.00 does not have mechanism for introducing variables that could | 
|  | // be bound. This means that ES SL 1.00 binding calls would be to | 
|  | // non-existing variable names.  Binding calls are only executed with ES SL | 
|  | // 3.00 and higher. | 
|  | for (auto const& output_var : fragment_shader->output_variable_list()) { | 
|  | size_t count = std::max(output_var.arraySize, 1u); | 
|  | bool is_array = output_var.arraySize > 0; | 
|  |  | 
|  | for (size_t jj = 0; jj < count; ++jj) { | 
|  | std::string name = output_var.name; | 
|  | std::string array_spec; | 
|  | if (is_array) { | 
|  | array_spec = std::string("[") + base::IntToString(jj) + "]"; | 
|  | name += array_spec; | 
|  | } | 
|  | auto it = bind_program_output_location_index_map_.find(name); | 
|  | if (it == bind_program_output_location_index_map_.end()) | 
|  | continue; | 
|  |  | 
|  | std::string mapped_name = output_var.mappedName; | 
|  | if (is_array) { | 
|  | mapped_name += array_spec; | 
|  | } | 
|  | const auto& binding = it->second; | 
|  | if (binding.second == 0) { | 
|  | // Handles the cases where client called glBindFragDataLocation as | 
|  | // well as glBindFragDataLocationIndexed with index == 0. | 
|  | glBindFragDataLocation(service_id_, binding.first, | 
|  | mapped_name.c_str()); | 
|  | } else { | 
|  | DCHECK(feature_info().feature_flags().ext_blend_func_extended); | 
|  | glBindFragDataLocationIndexed(service_id_, binding.first, | 
|  | binding.second, mapped_name.c_str()); | 
|  | } | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Support for EXT_blend_func_extended when used with ES SL 1.00 client | 
|  | // shader. | 
|  |  | 
|  | if (feature_info().gl_version_info().is_es || | 
|  | !feature_info().feature_flags().ext_blend_func_extended) | 
|  | return; | 
|  |  | 
|  | // The underlying context does not support EXT_blend_func_extended | 
|  | // natively, need to emulate it. | 
|  |  | 
|  | // ES SL 1.00 is the only language which contains GLSL built-ins | 
|  | // that need to be bound to color indices. If clients use other | 
|  | // languages, they also bind the output variables themselves. | 
|  | // Map gl_SecondaryFragColorEXT / gl_SecondaryFragDataEXT of | 
|  | // EXT_blend_func_extended to real color indexes. | 
|  | for (auto const& output_var : fragment_shader->output_variable_list()) { | 
|  | const std::string& name = output_var.mappedName; | 
|  | if (name == "gl_FragColor") { | 
|  | DCHECK_EQ(-1, output_var.location); | 
|  | DCHECK_EQ(0u, output_var.arraySize); | 
|  | // We leave these unbound by not giving a binding name. The driver will | 
|  | // bind this. | 
|  | } else if (name == "gl_FragData") { | 
|  | DCHECK_EQ(-1, output_var.location); | 
|  | DCHECK_NE(0u, output_var.arraySize); | 
|  | // We leave these unbound by not giving a binding name. The driver will | 
|  | // bind this. | 
|  | } else if (name == "gl_SecondaryFragColorEXT") { | 
|  | DCHECK_EQ(-1, output_var.location); | 
|  | DCHECK_EQ(0u, output_var.arraySize); | 
|  | glBindFragDataLocationIndexed(service_id_, 0, 1, | 
|  | "angle_SecondaryFragColor"); | 
|  | } else if (name == "gl_SecondaryFragDataEXT") { | 
|  | DCHECK_EQ(-1, output_var.location); | 
|  | DCHECK_NE(0u, output_var.arraySize); | 
|  | glBindFragDataLocationIndexed(service_id_, 0, 1, | 
|  | "angle_SecondaryFragData"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Program::Link(ShaderManager* manager, | 
|  | Program::VaryingsPackingOption varyings_packing_option, | 
|  | const ShaderCacheCallback& shader_callback) { | 
|  | ClearLinkStatus(); | 
|  |  | 
|  | if (!AttachedShadersExist()) { | 
|  | set_log_info("missing shaders"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | TimeTicks before_time = TimeTicks::Now(); | 
|  | bool link = true; | 
|  | ProgramCache* cache = manager_->program_cache_; | 
|  | if (cache) { | 
|  | DCHECK(!attached_shaders_[0]->last_compiled_source().empty() && | 
|  | !attached_shaders_[1]->last_compiled_source().empty()); | 
|  | ProgramCache::LinkedProgramStatus status = cache->GetLinkedProgramStatus( | 
|  | attached_shaders_[0]->last_compiled_signature(), | 
|  | attached_shaders_[1]->last_compiled_signature(), | 
|  | &bind_attrib_location_map_, | 
|  | transform_feedback_varyings_, | 
|  | transform_feedback_buffer_mode_); | 
|  |  | 
|  | bool cache_hit = status == ProgramCache::LINK_SUCCEEDED; | 
|  | UMA_HISTOGRAM_BOOLEAN("GPU.ProgramCache.CacheHit", cache_hit); | 
|  |  | 
|  | if (cache_hit) { | 
|  | ProgramCache::ProgramLoadResult success = | 
|  | cache->LoadLinkedProgram(service_id(), | 
|  | attached_shaders_[0].get(), | 
|  | attached_shaders_[1].get(), | 
|  | &bind_attrib_location_map_, | 
|  | transform_feedback_varyings_, | 
|  | transform_feedback_buffer_mode_, | 
|  | shader_callback); | 
|  | link = success != ProgramCache::PROGRAM_LOAD_SUCCESS; | 
|  | UMA_HISTOGRAM_BOOLEAN("GPU.ProgramCache.LoadBinarySuccess", !link); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (link) { | 
|  | CompileAttachedShaders(); | 
|  |  | 
|  | if (!CanLink()) { | 
|  | set_log_info("invalid shaders"); | 
|  | return false; | 
|  | } | 
|  | if (DetectShaderVersionMismatch()) { | 
|  | set_log_info("Versions of linked shaders have to match."); | 
|  | return false; | 
|  | } | 
|  | if (DetectAttribLocationBindingConflicts()) { | 
|  | set_log_info("glBindAttribLocation() conflicts"); | 
|  | return false; | 
|  | } | 
|  | std::string conflicting_name; | 
|  | if (DetectUniformsMismatch(&conflicting_name)) { | 
|  | std::string info_log = "Uniforms with the same name but different " | 
|  | "type/precision: " + conflicting_name; | 
|  | set_log_info(ProcessLogInfo(info_log).c_str()); | 
|  | return false; | 
|  | } | 
|  | if (DetectUniformLocationBindingConflicts()) { | 
|  | set_log_info("glBindUniformLocationCHROMIUM() conflicts"); | 
|  | return false; | 
|  | } | 
|  | if (DetectInterfaceBlocksMismatch(&conflicting_name)) { | 
|  | std::string info_log = | 
|  | "Interface blocks with the same name but different" | 
|  | " fields/layout: " + | 
|  | conflicting_name; | 
|  | set_log_info(ProcessLogInfo(info_log).c_str()); | 
|  | return false; | 
|  | } | 
|  | if (DetectVaryingsMismatch(&conflicting_name)) { | 
|  | std::string info_log = "Varyings with the same name but different type, " | 
|  | "or statically used varyings in fragment shader " | 
|  | "are not declared in vertex shader: " + | 
|  | conflicting_name; | 
|  | set_log_info(ProcessLogInfo(info_log).c_str()); | 
|  | return false; | 
|  | } | 
|  | if (DetectFragmentInputLocationBindingConflicts()) { | 
|  | set_log_info("glBindFragmentInputLocationCHROMIUM() conflicts"); | 
|  | return false; | 
|  | } | 
|  | if (DetectProgramOutputLocationBindingConflicts()) { | 
|  | set_log_info("glBindFragDataLocation() conflicts"); | 
|  | return false; | 
|  | } | 
|  | if (DetectBuiltInInvariantConflicts()) { | 
|  | set_log_info("Invariant settings for certain built-in varyings " | 
|  | "have to match"); | 
|  | return false; | 
|  | } | 
|  | if (DetectGlobalNameConflicts(&conflicting_name)) { | 
|  | std::string info_log = "Name conflicts between an uniform and an " | 
|  | "attribute: " + conflicting_name; | 
|  | set_log_info(ProcessLogInfo(info_log).c_str()); | 
|  | return false; | 
|  | } | 
|  | if (!CheckVaryingsPacking(varyings_packing_option)) { | 
|  | set_log_info("Varyings over maximum register limit"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ExecuteBindAttribLocationCalls(); | 
|  | if (!ExecuteTransformFeedbackVaryingsCall()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ExecuteProgramOutputBindCalls(); | 
|  |  | 
|  | before_time = TimeTicks::Now(); | 
|  | if (cache && gl::g_driver_gl.ext.b_GL_ARB_get_program_binary) { | 
|  | glProgramParameteri(service_id(), | 
|  | PROGRAM_BINARY_RETRIEVABLE_HINT, | 
|  | GL_TRUE); | 
|  | } | 
|  | glLinkProgram(service_id()); | 
|  | } | 
|  |  | 
|  | GLint success = 0; | 
|  | glGetProgramiv(service_id(), GL_LINK_STATUS, &success); | 
|  | if (success == GL_TRUE) { | 
|  | Update(); | 
|  | if (link) { | 
|  | // ANGLE updates the translated shader sources on link. | 
|  | for (auto shader : attached_shaders_) { | 
|  | shader->RefreshTranslatedShaderSource(); | 
|  | } | 
|  | if (cache) { | 
|  | cache->SaveLinkedProgram(service_id(), | 
|  | attached_shaders_[0].get(), | 
|  | attached_shaders_[1].get(), | 
|  | &bind_attrib_location_map_, | 
|  | transform_feedback_varyings_, | 
|  | transform_feedback_buffer_mode_, | 
|  | shader_callback); | 
|  | } | 
|  | UMA_HISTOGRAM_CUSTOM_COUNTS( | 
|  | "GPU.ProgramCache.BinaryCacheMissTime", | 
|  | static_cast<base::HistogramBase::Sample>( | 
|  | (TimeTicks::Now() - before_time).InMicroseconds()), | 
|  | 1, | 
|  | static_cast<base::HistogramBase::Sample>( | 
|  | TimeDelta::FromSeconds(10).InMicroseconds()), | 
|  | 50); | 
|  | } else { | 
|  | UMA_HISTOGRAM_CUSTOM_COUNTS( | 
|  | "GPU.ProgramCache.BinaryCacheHitTime", | 
|  | static_cast<base::HistogramBase::Sample>( | 
|  | (TimeTicks::Now() - before_time).InMicroseconds()), | 
|  | 1, | 
|  | static_cast<base::HistogramBase::Sample>( | 
|  | TimeDelta::FromSeconds(1).InMicroseconds()), | 
|  | 50); | 
|  | } | 
|  | } else { | 
|  | UpdateLogInfo(); | 
|  | } | 
|  | return success == GL_TRUE; | 
|  | } | 
|  |  | 
|  | void Program::Validate() { | 
|  | if (!IsValid()) { | 
|  | set_log_info("program not linked"); | 
|  | return; | 
|  | } | 
|  | glValidateProgram(service_id()); | 
|  | UpdateLogInfo(); | 
|  | } | 
|  |  | 
|  | GLint Program::GetUniformFakeLocation( | 
|  | const std::string& name) const { | 
|  | GLSLArrayName parsed_name(name); | 
|  |  | 
|  | for (const UniformInfo& info : uniform_infos_) { | 
|  | if (info.name == name || | 
|  | (info.is_array && | 
|  | info.name.compare(0, info.name.size() - 3, name) == 0)) { | 
|  | return info.fake_location_base; | 
|  | } 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) { | 
|  | DCHECK_GT(static_cast<int>(info.element_locations.size()), index); | 
|  | if (info.element_locations[index] == -1) | 
|  | return -1; | 
|  | return ProgramManager::MakeFakeLocation( | 
|  | info.fake_location_base, index); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | GLint Program::GetAttribLocation( | 
|  | const std::string& original_name) const { | 
|  | for (GLuint ii = 0; ii < attrib_infos_.size(); ++ii) { | 
|  | const VertexAttrib& info = attrib_infos_[ii]; | 
|  | if (info.name == original_name) { | 
|  | return info.location; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | const Program::UniformInfo* | 
|  | Program::GetUniformInfoByFakeLocation( | 
|  | GLint fake_location, GLint* real_location, GLint* array_index) const { | 
|  | DCHECK(real_location); | 
|  | DCHECK(array_index); | 
|  | if (fake_location < 0) | 
|  | return nullptr; | 
|  | size_t location_index = | 
|  | GetUniformLocationIndexFromFakeLocation(fake_location); | 
|  | if (location_index >= uniform_locations_.size()) | 
|  | return nullptr; | 
|  |  | 
|  | if (!uniform_locations_[location_index].IsActive()) | 
|  | return nullptr; | 
|  |  | 
|  | const UniformInfo* info = | 
|  | uniform_locations_[location_index].shader_variable(); | 
|  | size_t element_index = GetArrayElementIndexFromFakeLocation(fake_location); | 
|  | if (static_cast<GLsizei>(element_index) >= info->size) | 
|  | return nullptr; | 
|  | *real_location = info->element_locations[element_index]; | 
|  | *array_index = element_index; | 
|  | return info; | 
|  | } | 
|  |  | 
|  | bool Program::IsInactiveUniformLocationByFakeLocation( | 
|  | GLint fake_location) const { | 
|  | if (fake_location < 0) | 
|  | return true; | 
|  | size_t location_index = | 
|  | GetUniformLocationIndexFromFakeLocation(fake_location); | 
|  | if (location_index >= uniform_locations_.size()) | 
|  | return false; | 
|  | return uniform_locations_[location_index].IsInactive(); | 
|  | } | 
|  |  | 
|  | const std::string* Program::GetAttribMappedName( | 
|  | const std::string& original_name) const { | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (shader) { | 
|  | const std::string* mapped_name = | 
|  | shader->GetAttribMappedName(original_name); | 
|  | if (mapped_name) | 
|  | return mapped_name; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const std::string* Program::GetUniformMappedName( | 
|  | const std::string& original_name) const { | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (shader) { | 
|  | const std::string* mapped_name = | 
|  | shader->GetUniformMappedName(original_name); | 
|  | if (mapped_name) | 
|  | return mapped_name; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const std::string* Program::GetOriginalNameFromHashedName( | 
|  | const std::string& hashed_name) const { | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (shader) { | 
|  | const std::string* original_name = | 
|  | shader->GetOriginalNameFromHashedName(hashed_name); | 
|  | if (original_name) | 
|  | return original_name; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const Program::FragmentInputInfo* Program::GetFragmentInputInfoByFakeLocation( | 
|  | GLint fake_location) const { | 
|  | if (fake_location < 0) | 
|  | return nullptr; | 
|  | size_t location_index = static_cast<size_t>(fake_location); | 
|  | if (location_index >= fragment_input_locations_.size()) | 
|  | return nullptr; | 
|  | if (!fragment_input_locations_[location_index].IsActive()) | 
|  | return nullptr; | 
|  | return fragment_input_locations_[location_index].shader_variable(); | 
|  | } | 
|  |  | 
|  | bool Program::IsInactiveFragmentInputLocationByFakeLocation( | 
|  | GLint fake_location) const { | 
|  | if (fake_location < 0) | 
|  | return true; | 
|  | size_t location_index = static_cast<size_t>(fake_location); | 
|  | if (location_index >= fragment_input_locations_.size()) | 
|  | return false; | 
|  | return fragment_input_locations_[location_index].IsInactive(); | 
|  | } | 
|  |  | 
|  | bool Program::SetUniformLocationBinding( | 
|  | const std::string& name, GLint location) { | 
|  | std::string short_name; | 
|  | int element_index = 0; | 
|  | if (!GetUniformNameSansElement(name, &element_index, &short_name) || | 
|  | element_index != 0) { | 
|  | return false; | 
|  | } | 
|  | bind_uniform_location_map_[short_name] = location; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Program::SetFragmentInputLocationBinding(const std::string& name, | 
|  | GLint location) { | 
|  | // The client wants to bind either "name" or "name[0]". | 
|  | // GL ES 3.1 spec refers to active array names with language such as: | 
|  | // "if the string identifies the base name of an active array, where the | 
|  | // string would exactly match the name of the variable if the suffix "[0]" | 
|  | // were appended to the string". | 
|  |  | 
|  | // At this point we can not know if the string identifies a simple variable, | 
|  | // a base name of an array, or nothing.  Store both, so if user overwrites | 
|  | // either, both still work correctly. | 
|  | bind_fragment_input_location_map_[name] = location; | 
|  | bind_fragment_input_location_map_[name + "[0]"] = location; | 
|  | } | 
|  |  | 
|  | void Program::SetProgramOutputLocationBinding(const std::string& name, | 
|  | GLuint color_name) { | 
|  | SetProgramOutputLocationIndexedBinding(name, color_name, 0); | 
|  | } | 
|  |  | 
|  | void Program::SetProgramOutputLocationIndexedBinding(const std::string& name, | 
|  | GLuint color_name, | 
|  | GLuint index) { | 
|  | bind_program_output_location_index_map_[name] = | 
|  | std::make_pair(color_name, index); | 
|  | bind_program_output_location_index_map_[name + "[0]"] = | 
|  | std::make_pair(color_name, index); | 
|  | } | 
|  |  | 
|  | void Program::GetVertexAttribData( | 
|  | const std::string& name, std::string* original_name, GLenum* type) const { | 
|  | DCHECK(original_name); | 
|  | DCHECK(type); | 
|  | Shader* shader = attached_shaders_[ShaderTypeToIndex(GL_VERTEX_SHADER)].get(); | 
|  | if (shader) { | 
|  | // Vertex attributes can not be arrays or structs (GLSL ES 3.00.4, section | 
|  | // 4.3.4, "Input Variables"), so the top level sh::Attribute returns the | 
|  | // information we need. | 
|  | const sh::Attribute* info = shader->GetAttribInfo(name); | 
|  | if (info) { | 
|  | *original_name = info->name; | 
|  | *type = info->type; | 
|  | return; | 
|  | } | 
|  | } | 
|  | // TODO(zmo): this path should never be reached unless there is a serious | 
|  | // bug in the driver or in ANGLE translator. | 
|  | *original_name = name; | 
|  | } | 
|  |  | 
|  | const Program::UniformInfo* | 
|  | Program::GetUniformInfo( | 
|  | GLint index) const { | 
|  | if (static_cast<size_t>(index) >= uniform_infos_.size()) { | 
|  | return NULL; | 
|  | } | 
|  | return &uniform_infos_[index]; | 
|  | } | 
|  |  | 
|  | bool Program::SetSamplers( | 
|  | GLint num_texture_units, GLint fake_location, | 
|  | GLsizei count, const GLint* value) { | 
|  | // The caller has checked that the location is active and valid. | 
|  | DCHECK(fake_location >= 0); | 
|  | size_t location_index = | 
|  | GetUniformLocationIndexFromFakeLocation(fake_location); | 
|  | DCHECK(location_index < uniform_locations_.size()); | 
|  | DCHECK(uniform_locations_[location_index].IsActive()); | 
|  |  | 
|  | UniformInfo* info = uniform_locations_[location_index].shader_variable(); | 
|  |  | 
|  | size_t element_index = GetArrayElementIndexFromFakeLocation(fake_location); | 
|  | if (static_cast<GLsizei>(element_index) >= info->size) | 
|  | return true; | 
|  | count = std::min(info->size - static_cast<GLsizei>(element_index), count); | 
|  | if (info->IsSampler() && count > 0) { | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | if (value[ii] < 0 || value[ii] >= num_texture_units) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | std::copy(value, value + count, | 
|  | info->texture_units.begin() + element_index); | 
|  | return true; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Program::GetProgramiv(GLenum pname, GLint* params) { | 
|  | switch (pname) { | 
|  | case GL_ACTIVE_ATTRIBUTES: | 
|  | *params = attrib_infos_.size(); | 
|  | break; | 
|  | case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: | 
|  | // Notice +1 to accomodate NULL terminator. | 
|  | *params = max_attrib_name_length_ + 1; | 
|  | break; | 
|  | case GL_ACTIVE_UNIFORMS: | 
|  | *params = uniform_infos_.size(); | 
|  | break; | 
|  | case GL_ACTIVE_UNIFORM_MAX_LENGTH: | 
|  | // Notice +1 to accomodate NULL terminator. | 
|  | *params = max_uniform_name_length_ + 1; | 
|  | break; | 
|  | case GL_LINK_STATUS: | 
|  | *params = link_status_; | 
|  | break; | 
|  | case GL_INFO_LOG_LENGTH: | 
|  | // Notice +1 to accomodate NULL terminator. | 
|  | *params = log_info_.get() ? (log_info_->size() + 1) : 0; | 
|  | break; | 
|  | case GL_DELETE_STATUS: | 
|  | *params = deleted_; | 
|  | break; | 
|  | case GL_VALIDATE_STATUS: | 
|  | if (!IsValid()) { | 
|  | *params = GL_FALSE; | 
|  | } else { | 
|  | glGetProgramiv(service_id_, pname, params); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | glGetProgramiv(service_id_, pname, params); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Program::AttachShader( | 
|  | ShaderManager* shader_manager, | 
|  | Shader* shader) { | 
|  | DCHECK(shader_manager); | 
|  | DCHECK(shader); | 
|  | int index = ShaderTypeToIndex(shader->shader_type()); | 
|  | if (attached_shaders_[index].get() != NULL) { | 
|  | return false; | 
|  | } | 
|  | attached_shaders_[index] = scoped_refptr<Shader>(shader); | 
|  | shader_manager->UseShader(shader); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::IsShaderAttached(Shader* shader) { | 
|  | return attached_shaders_[ShaderTypeToIndex(shader->shader_type())].get() == | 
|  | shader; | 
|  | } | 
|  |  | 
|  | void Program::DetachShader( | 
|  | ShaderManager* shader_manager, | 
|  | Shader* shader) { | 
|  | DCHECK(shader_manager); | 
|  | DCHECK(shader); | 
|  | DCHECK(IsShaderAttached(shader)); | 
|  | attached_shaders_[ShaderTypeToIndex(shader->shader_type())] = NULL; | 
|  | shader_manager->UnuseShader(shader); | 
|  | } | 
|  |  | 
|  | void Program::DetachShaders(ShaderManager* shader_manager) { | 
|  | DCHECK(shader_manager); | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (shader) { | 
|  | DetachShader(shader_manager, shader.get()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::CompileAttachedShaders() { | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (shader) { | 
|  | shader->DoCompile(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Program::AttachedShadersExist() const { | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (!shader) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::CanLink() const { | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (!shader || !shader->valid()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::DetectShaderVersionMismatch() const { | 
|  | int version = Shader::kUndefinedShaderVersion; | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (shader) { | 
|  | if (version != Shader::kUndefinedShaderVersion && | 
|  | shader->shader_version() != version) { | 
|  | return true; | 
|  | } | 
|  | version = shader->shader_version(); | 
|  | DCHECK(version != Shader::kUndefinedShaderVersion); | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectAttribLocationBindingConflicts() const { | 
|  | std::set<GLint> location_binding_used; | 
|  | for (const auto& key_value : bind_attrib_location_map_) { | 
|  | // Find out if an attribute is statically used in this program's shaders. | 
|  | const sh::Attribute* attrib = NULL; | 
|  | const std::string* mapped_name = GetAttribMappedName(key_value.first); | 
|  | if (!mapped_name) | 
|  | continue; | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (!shader || !shader->valid()) | 
|  | continue; | 
|  | attrib = shader->GetAttribInfo(*mapped_name); | 
|  | if (attrib) { | 
|  | if (attrib->staticUse) | 
|  | break; | 
|  | else | 
|  | attrib = NULL; | 
|  | } | 
|  | } | 
|  | if (attrib) { | 
|  | size_t num_of_locations = 1; | 
|  | switch (attrib->type) { | 
|  | case GL_FLOAT_MAT2: | 
|  | num_of_locations = 2; | 
|  | break; | 
|  | case GL_FLOAT_MAT3: | 
|  | num_of_locations = 3; | 
|  | break; | 
|  | case GL_FLOAT_MAT4: | 
|  | num_of_locations = 4; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | for (size_t ii = 0; ii < num_of_locations; ++ii) { | 
|  | GLint loc = key_value.second + ii; | 
|  | auto result = location_binding_used.insert(loc); | 
|  | if (!result.second) | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectUniformLocationBindingConflicts() const { | 
|  | std::set<GLint> location_binding_used; | 
|  | for (auto it : bind_uniform_location_map_) { | 
|  | // Find out if an attribute is statically used in this program's shaders. | 
|  | const sh::Uniform* uniform = nullptr; | 
|  | const std::string* mapped_name = GetUniformMappedName(it.first); | 
|  | if (!mapped_name) | 
|  | continue; | 
|  | for (auto shader : attached_shaders_) { | 
|  | if (!shader || !shader->valid()) | 
|  | continue; | 
|  | uniform = shader->GetUniformInfo(*mapped_name); | 
|  | if (uniform) { | 
|  | if (uniform->staticUse) | 
|  | break; | 
|  | else | 
|  | uniform = nullptr; | 
|  | } | 
|  | } | 
|  | if (uniform) { | 
|  | auto result = location_binding_used.insert(it.second); | 
|  | if (!result.second) | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectUniformsMismatch(std::string* conflicting_name) const { | 
|  | typedef std::map<std::string, const sh::Uniform*> UniformPointerMap; | 
|  | UniformPointerMap uniform_pointer_map; | 
|  | for (auto shader : attached_shaders_) { | 
|  | const UniformMap& shader_uniforms = shader->uniform_map(); | 
|  | for (const auto& key_value : shader_uniforms) { | 
|  | const std::string& name = key_value.first; | 
|  | UniformPointerMap::iterator hit = uniform_pointer_map.find(name); | 
|  | if (hit == uniform_pointer_map.end()) { | 
|  | uniform_pointer_map[name] = &(key_value.second); | 
|  | } else { | 
|  | // If a uniform is in the map, i.e., it has already been declared by | 
|  | // another shader, then the type, precision, etc. must match. | 
|  | if (hit->second->isSameUniformAtLinkTime(key_value.second)) | 
|  | continue; | 
|  | *conflicting_name = name; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectInterfaceBlocksMismatch( | 
|  | std::string* conflicting_name) const { | 
|  | std::map<std::string, const sh::InterfaceBlock*> interface_pointer_map; | 
|  | for (auto shader : attached_shaders_) { | 
|  | const InterfaceBlockMap& shader_interfaces = shader->interface_block_map(); | 
|  | for (const auto& it : shader_interfaces) { | 
|  | const auto& name = it.first; | 
|  | auto hit = interface_pointer_map.find(name); | 
|  | if (hit == interface_pointer_map.end()) { | 
|  | interface_pointer_map[name] = &(it.second); | 
|  | } else { | 
|  | // If an interface is in the map, i.e., it has already been declared by | 
|  | // another shader, then the layout must match. | 
|  | if (hit->second->isSameInterfaceBlockAtLinkTime(it.second)) | 
|  | continue; | 
|  | *conflicting_name = name; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectVaryingsMismatch(std::string* conflicting_name) const { | 
|  | DCHECK(attached_shaders_[0].get() && | 
|  | attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && | 
|  | attached_shaders_[1].get() && | 
|  | attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER); | 
|  | const VaryingMap* vertex_varyings = &(attached_shaders_[0]->varying_map()); | 
|  | const VaryingMap* fragment_varyings = &(attached_shaders_[1]->varying_map()); | 
|  |  | 
|  | int shader_version = attached_shaders_[0]->shader_version(); | 
|  |  | 
|  | for (const auto& key_value : *fragment_varyings) { | 
|  | const std::string& name = key_value.first; | 
|  | if (IsBuiltInFragmentVarying(name)) | 
|  | continue; | 
|  |  | 
|  | VaryingMap::const_iterator hit = vertex_varyings->find(name); | 
|  | if (hit == vertex_varyings->end()) { | 
|  | if (key_value.second.staticUse) { | 
|  | *conflicting_name = name; | 
|  | return true; | 
|  | } | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!hit->second.isSameVaryingAtLinkTime(key_value.second, | 
|  | shader_version)) { | 
|  | *conflicting_name = name; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectFragmentInputLocationBindingConflicts() const { | 
|  | auto* shader = attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); | 
|  | if (!shader || !shader->valid()) | 
|  | return false; | 
|  |  | 
|  | std::set<GLint> location_binding_used; | 
|  | for (auto it : bind_fragment_input_location_map_) { | 
|  | // Find out if an fragment input is statically used in this program's | 
|  | // shaders. | 
|  | const std::string* mapped_name = shader->GetVaryingMappedName(it.first); | 
|  | if (!mapped_name) | 
|  | continue; | 
|  | const sh::Varying* fragment_input = shader->GetVaryingInfo(*mapped_name); | 
|  | if (fragment_input && fragment_input->staticUse) { | 
|  | auto result = location_binding_used.insert(it.second); | 
|  | if (!result.second) | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectProgramOutputLocationBindingConflicts() const { | 
|  | if (feature_info().disable_shader_translator()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Shader* shader = | 
|  | attached_shaders_[ShaderTypeToIndex(GL_FRAGMENT_SHADER)].get(); | 
|  | DCHECK(shader && shader->valid()); | 
|  |  | 
|  | if (shader->shader_version() == 100) | 
|  | return false; | 
|  |  | 
|  | std::set<LocationIndexMap::mapped_type> location_binding_used; | 
|  | for (auto const& output_var : shader->output_variable_list()) { | 
|  | if (!output_var.staticUse) | 
|  | continue; | 
|  |  | 
|  | size_t count = std::max(output_var.arraySize, 1u); | 
|  | bool is_array = output_var.arraySize > 0; | 
|  |  | 
|  | for (size_t jj = 0; jj < count; ++jj) { | 
|  | std::string name = output_var.name; | 
|  | if (is_array) | 
|  | name += std::string("[") + base::IntToString(jj) + "]"; | 
|  |  | 
|  | auto it = bind_program_output_location_index_map_.find(name); | 
|  | if (it == bind_program_output_location_index_map_.end()) | 
|  | continue; | 
|  | auto result = location_binding_used.insert(it->second); | 
|  | if (!result.second) | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::DetectBuiltInInvariantConflicts() const { | 
|  | DCHECK(attached_shaders_[0].get() && | 
|  | attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && | 
|  | attached_shaders_[1].get() && | 
|  | attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER); | 
|  | const VaryingMap& vertex_varyings = attached_shaders_[0]->varying_map(); | 
|  | const VaryingMap& fragment_varyings = attached_shaders_[1]->varying_map(); | 
|  |  | 
|  | bool gl_position_invariant = IsBuiltInInvariant( | 
|  | vertex_varyings, "gl_Position"); | 
|  | bool gl_point_size_invariant = IsBuiltInInvariant( | 
|  | vertex_varyings, "gl_PointSize"); | 
|  |  | 
|  | bool gl_frag_coord_invariant = IsBuiltInInvariant( | 
|  | fragment_varyings, "gl_FragCoord"); | 
|  | bool gl_point_coord_invariant = IsBuiltInInvariant( | 
|  | fragment_varyings, "gl_PointCoord"); | 
|  |  | 
|  | return ((gl_frag_coord_invariant && !gl_position_invariant) || | 
|  | (gl_point_coord_invariant && !gl_point_size_invariant)); | 
|  | } | 
|  |  | 
|  | bool Program::DetectGlobalNameConflicts(std::string* conflicting_name) const { | 
|  | DCHECK(attached_shaders_[0].get() && | 
|  | attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && | 
|  | attached_shaders_[1].get() && | 
|  | attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER); | 
|  | const UniformMap* uniforms[2]; | 
|  | uniforms[0] = &(attached_shaders_[0]->uniform_map()); | 
|  | uniforms[1] = &(attached_shaders_[1]->uniform_map()); | 
|  | const AttributeMap* attribs = | 
|  | &(attached_shaders_[0]->attrib_map()); | 
|  |  | 
|  | for (const auto& key_value : *attribs) { | 
|  | for (int ii = 0; ii < 2; ++ii) { | 
|  | if (uniforms[ii]->find(key_value.first) != uniforms[ii]->end()) { | 
|  | *conflicting_name = key_value.first; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Program::CheckVaryingsPacking( | 
|  | Program::VaryingsPackingOption option) const { | 
|  | DCHECK(attached_shaders_[0].get() && | 
|  | attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && | 
|  | attached_shaders_[1].get() && | 
|  | attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER); | 
|  | const VaryingMap* vertex_varyings = &(attached_shaders_[0]->varying_map()); | 
|  | const VaryingMap* fragment_varyings = &(attached_shaders_[1]->varying_map()); | 
|  |  | 
|  | std::map<std::string, const sh::ShaderVariable*> combined_map; | 
|  |  | 
|  | for (const auto& key_value : *fragment_varyings) { | 
|  | if (!key_value.second.staticUse && option == kCountOnlyStaticallyUsed) | 
|  | continue; | 
|  | if (!IsBuiltInFragmentVarying(key_value.first)) { | 
|  | VaryingMap::const_iterator vertex_iter = | 
|  | vertex_varyings->find(key_value.first); | 
|  | if (vertex_iter == vertex_varyings->end() || | 
|  | (!vertex_iter->second.staticUse && | 
|  | option == kCountOnlyStaticallyUsed)) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | combined_map[key_value.first] = &key_value.second; | 
|  | } | 
|  |  | 
|  | if (combined_map.size() == 0) | 
|  | return true; | 
|  | std::vector<sh::ShaderVariable> variables; | 
|  | for (const auto& key_value : combined_map) { | 
|  | variables.push_back(*key_value.second); | 
|  | } | 
|  | return ShCheckVariablesWithinPackingLimits( | 
|  | static_cast<int>(manager_->max_varying_vectors()), | 
|  | variables); | 
|  | } | 
|  |  | 
|  | void Program::GetProgramInfo( | 
|  | ProgramManager* manager, CommonDecoder::Bucket* bucket) const { | 
|  | // NOTE: It seems to me the math in here does not need check for overflow | 
|  | // because the data being calucated from has various small limits. The max | 
|  | // number of attribs + uniforms is somewhere well under 1024. The maximum size | 
|  | // of an identifier is 256 characters. | 
|  | uint32_t num_locations = 0; | 
|  | uint32_t total_string_size = 0; | 
|  |  | 
|  | for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) { | 
|  | const VertexAttrib& info = attrib_infos_[ii]; | 
|  | num_locations += 1; | 
|  | total_string_size += info.name.size(); | 
|  | } | 
|  |  | 
|  | for (const UniformInfo& info : uniform_infos_) { | 
|  | num_locations += info.element_locations.size(); | 
|  | total_string_size += info.name.size(); | 
|  | } | 
|  |  | 
|  | uint32_t num_inputs = attrib_infos_.size() + uniform_infos_.size(); | 
|  | uint32_t input_size = num_inputs * sizeof(ProgramInput); | 
|  | uint32_t location_size = num_locations * sizeof(int32_t); | 
|  | uint32_t size = sizeof(ProgramInfoHeader) + input_size + location_size + | 
|  | total_string_size; | 
|  |  | 
|  | bucket->SetSize(size); | 
|  | ProgramInfoHeader* header = bucket->GetDataAs<ProgramInfoHeader*>(0, size); | 
|  | ProgramInput* inputs = bucket->GetDataAs<ProgramInput*>( | 
|  | sizeof(ProgramInfoHeader), input_size); | 
|  | int32_t* locations = bucket->GetDataAs<int32_t*>( | 
|  | sizeof(ProgramInfoHeader) + input_size, location_size); | 
|  | char* strings = bucket->GetDataAs<char*>( | 
|  | sizeof(ProgramInfoHeader) + input_size + location_size, | 
|  | total_string_size); | 
|  | DCHECK(header); | 
|  | DCHECK(inputs); | 
|  | DCHECK(locations); | 
|  | DCHECK(strings); | 
|  |  | 
|  | header->link_status = link_status_; | 
|  | header->num_attribs = attrib_infos_.size(); | 
|  | header->num_uniforms = uniform_infos_.size(); | 
|  |  | 
|  | for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) { | 
|  | const VertexAttrib& info = attrib_infos_[ii]; | 
|  | inputs->size = info.size; | 
|  | inputs->type = info.type; | 
|  | inputs->location_offset = ComputeOffset(header, locations); | 
|  | inputs->name_offset = ComputeOffset(header, strings); | 
|  | inputs->name_length = info.name.size(); | 
|  | *locations++ = info.location; | 
|  | memcpy(strings, info.name.c_str(), info.name.size()); | 
|  | strings += info.name.size(); | 
|  | ++inputs; | 
|  | } | 
|  |  | 
|  | for (const UniformInfo& info : uniform_infos_) { | 
|  | inputs->size = info.size; | 
|  | inputs->type = info.type; | 
|  | inputs->location_offset = ComputeOffset(header, locations); | 
|  | inputs->name_offset = ComputeOffset(header, strings); | 
|  | inputs->name_length = info.name.size(); | 
|  | DCHECK(static_cast<size_t>(info.size) == info.element_locations.size()); | 
|  | for (size_t jj = 0; jj < info.element_locations.size(); ++jj) { | 
|  | if (info.element_locations[jj] == -1) | 
|  | *locations++ = -1; | 
|  | else | 
|  | *locations++ = | 
|  | ProgramManager::MakeFakeLocation(info.fake_location_base, jj); | 
|  | } | 
|  | memcpy(strings, info.name.c_str(), info.name.size()); | 
|  | strings += info.name.size(); | 
|  | ++inputs; | 
|  | } | 
|  | // NOTE: currently we do not pass inactive uniform binding locations | 
|  | // through the program info call. | 
|  |  | 
|  | // NOTE: currently we do not pass fragment input infos through the program | 
|  | // info call, because they are not exposed through any getter function. | 
|  |  | 
|  | DCHECK_EQ(ComputeOffset(header, strings), size); | 
|  | } | 
|  |  | 
|  | bool Program::GetUniformBlocks(CommonDecoder::Bucket* bucket) const { | 
|  | // The data is packed into the bucket in the following order | 
|  | //   1) header | 
|  | //   2) N entries of block data (except for name and indices) | 
|  | //   3) name1, indices1, name2, indices2, ..., nameN, indicesN | 
|  | // | 
|  | // We query all the data directly through GL calls, assuming they are | 
|  | // cheap through MANGLE. | 
|  |  | 
|  | DCHECK(bucket); | 
|  | GLuint program = service_id(); | 
|  |  | 
|  | uint32_t header_size = sizeof(UniformBlocksHeader); | 
|  | bucket->SetSize(header_size);  // In case we fail. | 
|  |  | 
|  | uint32_t num_uniform_blocks = 0; | 
|  | GLint param = GL_FALSE; | 
|  | // We assume program is a valid program service id. | 
|  | glGetProgramiv(program, GL_LINK_STATUS, ¶m); | 
|  | if (param == GL_TRUE) { | 
|  | param = 0; | 
|  | glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, ¶m); | 
|  | num_uniform_blocks = static_cast<uint32_t>(param); | 
|  | } | 
|  | if (num_uniform_blocks == 0) { | 
|  | // Although spec allows an implementation to return uniform block info | 
|  | // even if a link fails, for consistency, we disallow that. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::vector<UniformBlockInfo> blocks(num_uniform_blocks); | 
|  | base::CheckedNumeric<uint32_t> size = sizeof(UniformBlockInfo); | 
|  | size *= num_uniform_blocks; | 
|  | uint32_t entry_size = size.ValueOrDefault(0); | 
|  | size += header_size; | 
|  | std::vector<std::string> names(num_uniform_blocks); | 
|  | GLint max_name_length = 0; | 
|  | glGetProgramiv( | 
|  | program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_name_length); | 
|  | std::vector<GLchar> buffer(max_name_length); | 
|  | GLsizei length; | 
|  | for (uint32_t ii = 0; ii < num_uniform_blocks; ++ii) { | 
|  | param = 0; | 
|  | glGetActiveUniformBlockiv(program, ii, GL_UNIFORM_BLOCK_BINDING, ¶m); | 
|  | blocks[ii].binding = static_cast<uint32_t>(param); | 
|  |  | 
|  | param = 0; | 
|  | glGetActiveUniformBlockiv(program, ii, GL_UNIFORM_BLOCK_DATA_SIZE, ¶m); | 
|  | blocks[ii].data_size = static_cast<uint32_t>(param); | 
|  |  | 
|  | blocks[ii].name_offset = size.ValueOrDefault(0); | 
|  | param = 0; | 
|  | glGetActiveUniformBlockiv( | 
|  | program, ii, GL_UNIFORM_BLOCK_NAME_LENGTH, ¶m); | 
|  | DCHECK_GE(max_name_length, param); | 
|  | memset(&buffer[0], 0, param); | 
|  | length = 0; | 
|  | glGetActiveUniformBlockName( | 
|  | program, ii, static_cast<GLsizei>(param), &length, &buffer[0]); | 
|  | DCHECK_EQ(param, length + 1); | 
|  | names[ii] = std::string(&buffer[0], length); | 
|  | // TODO(zmo): optimize the name mapping lookup. | 
|  | size_t pos = names[ii].find_first_of('['); | 
|  | const std::string* original_name; | 
|  | std::string array_index_str = ""; | 
|  | if (pos != std::string::npos) { | 
|  | original_name = GetOriginalNameFromHashedName(names[ii].substr(0, pos)); | 
|  | array_index_str = names[ii].substr(pos); | 
|  | } else { | 
|  | original_name = GetOriginalNameFromHashedName(names[ii]); | 
|  | } | 
|  | if (original_name) | 
|  | names[ii] = *original_name + array_index_str; | 
|  | blocks[ii].name_length = names[ii].size() + 1; | 
|  | size += blocks[ii].name_length; | 
|  |  | 
|  | param = 0; | 
|  | glGetActiveUniformBlockiv( | 
|  | program, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, ¶m); | 
|  | blocks[ii].active_uniforms = static_cast<uint32_t>(param); | 
|  | blocks[ii].active_uniform_offset = size.ValueOrDefault(0); | 
|  | base::CheckedNumeric<uint32_t> indices_size = blocks[ii].active_uniforms; | 
|  | indices_size *= sizeof(uint32_t); | 
|  | if (!indices_size.IsValid()) | 
|  | return false; | 
|  | size += indices_size.ValueOrDefault(0); | 
|  |  | 
|  | param = 0; | 
|  | glGetActiveUniformBlockiv( | 
|  | program, ii, GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, ¶m); | 
|  | blocks[ii].referenced_by_vertex_shader = static_cast<uint32_t>(param); | 
|  |  | 
|  | param = 0; | 
|  | glGetActiveUniformBlockiv( | 
|  | program, ii, GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, ¶m); | 
|  | blocks[ii].referenced_by_fragment_shader = static_cast<uint32_t>(param); | 
|  | } | 
|  | if (!size.IsValid()) | 
|  | return false; | 
|  | uint32_t total_size = size.ValueOrDefault(0); | 
|  | DCHECK_LE(header_size + entry_size, total_size); | 
|  | uint32_t data_size = total_size - header_size - entry_size; | 
|  |  | 
|  | bucket->SetSize(total_size); | 
|  | UniformBlocksHeader* header = | 
|  | bucket->GetDataAs<UniformBlocksHeader*>(0, header_size); | 
|  | UniformBlockInfo* entries = bucket->GetDataAs<UniformBlockInfo*>( | 
|  | header_size, entry_size); | 
|  | char* data = bucket->GetDataAs<char*>(header_size + entry_size, data_size); | 
|  | DCHECK(header); | 
|  | DCHECK(entries); | 
|  | DCHECK(data); | 
|  |  | 
|  | // Copy over data for the header and entries. | 
|  | header->num_uniform_blocks = num_uniform_blocks; | 
|  | memcpy(entries, &blocks[0], entry_size); | 
|  |  | 
|  | std::vector<GLint> params; | 
|  | for (uint32_t ii = 0; ii < num_uniform_blocks; ++ii) { | 
|  | // Get active uniform name. | 
|  | memcpy(data, names[ii].c_str(), names[ii].length() + 1); | 
|  | data += names[ii].length() + 1; | 
|  |  | 
|  | // Get active uniform indices. | 
|  | if (params.size() < blocks[ii].active_uniforms) | 
|  | params.resize(blocks[ii].active_uniforms); | 
|  | uint32_t num_bytes = blocks[ii].active_uniforms * sizeof(GLint); | 
|  | memset(¶ms[0], 0, num_bytes); | 
|  | glGetActiveUniformBlockiv( | 
|  | program, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ¶ms[0]); | 
|  | uint32_t* indices = reinterpret_cast<uint32_t*>(data); | 
|  | for (uint32_t uu = 0; uu < blocks[ii].active_uniforms; ++uu) { | 
|  | indices[uu] = static_cast<uint32_t>(params[uu]); | 
|  | } | 
|  | data += num_bytes; | 
|  | } | 
|  | DCHECK_EQ(ComputeOffset(header, data), total_size); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::GetTransformFeedbackVaryings( | 
|  | CommonDecoder::Bucket* bucket) const { | 
|  | // The data is packed into the bucket in the following order | 
|  | //   1) header | 
|  | //   2) N entries of varying data (except for name) | 
|  | //   3) name1, name2, ..., nameN | 
|  | // | 
|  | // We query all the data directly through GL calls, assuming they are | 
|  | // cheap through MANGLE. | 
|  |  | 
|  | DCHECK(bucket); | 
|  | GLuint program = service_id(); | 
|  |  | 
|  | uint32_t header_size = sizeof(TransformFeedbackVaryingsHeader); | 
|  | bucket->SetSize(header_size);  // In case we fail. | 
|  |  | 
|  | GLenum transform_feedback_buffer_mode = 0; | 
|  | GLint param = 0; | 
|  | glGetProgramiv(program, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, ¶m); | 
|  | transform_feedback_buffer_mode = static_cast<GLenum>(param); | 
|  |  | 
|  | uint32_t num_transform_feedback_varyings = 0; | 
|  | param = GL_FALSE; | 
|  | // We assume program is a valid program service id. | 
|  | glGetProgramiv(program, GL_LINK_STATUS, ¶m); | 
|  | if (param == GL_TRUE) { | 
|  | param = 0; | 
|  | glGetProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYINGS, ¶m); | 
|  | num_transform_feedback_varyings = static_cast<uint32_t>(param); | 
|  | } | 
|  | if (num_transform_feedback_varyings == 0) { | 
|  | TransformFeedbackVaryingsHeader* header = | 
|  | bucket->GetDataAs<TransformFeedbackVaryingsHeader*>(0, header_size); | 
|  | header->transform_feedback_buffer_mode = transform_feedback_buffer_mode; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::vector<TransformFeedbackVaryingInfo> varyings( | 
|  | num_transform_feedback_varyings); | 
|  | base::CheckedNumeric<uint32_t> size = sizeof(TransformFeedbackVaryingInfo); | 
|  | size *= num_transform_feedback_varyings; | 
|  | uint32_t entry_size = size.ValueOrDefault(0); | 
|  | size += header_size; | 
|  | std::vector<std::string> names(num_transform_feedback_varyings); | 
|  | GLint max_name_length = 0; | 
|  | glGetProgramiv( | 
|  | program, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &max_name_length); | 
|  | if (max_name_length < 1) | 
|  | max_name_length = 1; | 
|  | std::vector<char> buffer(max_name_length); | 
|  | for (uint32_t ii = 0; ii < num_transform_feedback_varyings; ++ii) { | 
|  | GLsizei var_size = 0; | 
|  | GLsizei var_name_length = 0; | 
|  | GLenum var_type = 0; | 
|  | glGetTransformFeedbackVarying( | 
|  | program, ii, max_name_length, | 
|  | &var_name_length, &var_size, &var_type, &buffer[0]); | 
|  | varyings[ii].size = static_cast<uint32_t>(var_size); | 
|  | varyings[ii].type = static_cast<uint32_t>(var_type); | 
|  | varyings[ii].name_offset = static_cast<uint32_t>(size.ValueOrDefault(0)); | 
|  | DCHECK_GT(max_name_length, var_name_length); | 
|  | names[ii] = std::string(&buffer[0], var_name_length); | 
|  | // TODO(zmo): optimize the name mapping lookup. | 
|  | const std::string* original_name = GetOriginalNameFromHashedName(names[ii]); | 
|  | if (original_name) | 
|  | names[ii] = *original_name; | 
|  | varyings[ii].name_length = names[ii].size() + 1; | 
|  | size += names[ii].size(); | 
|  | size += 1; | 
|  | } | 
|  | if (!size.IsValid()) | 
|  | return false; | 
|  | uint32_t total_size = size.ValueOrDefault(0); | 
|  | DCHECK_LE(header_size + entry_size, total_size); | 
|  | uint32_t data_size = total_size - header_size - entry_size; | 
|  |  | 
|  | bucket->SetSize(total_size); | 
|  | TransformFeedbackVaryingsHeader* header = | 
|  | bucket->GetDataAs<TransformFeedbackVaryingsHeader*>(0, header_size); | 
|  | TransformFeedbackVaryingInfo* entries = | 
|  | bucket->GetDataAs<TransformFeedbackVaryingInfo*>(header_size, entry_size); | 
|  | char* data = bucket->GetDataAs<char*>(header_size + entry_size, data_size); | 
|  | DCHECK(header); | 
|  | DCHECK(entries); | 
|  | DCHECK(data); | 
|  |  | 
|  | // Copy over data for the header and entries. | 
|  | header->transform_feedback_buffer_mode = transform_feedback_buffer_mode; | 
|  | header->num_transform_feedback_varyings = num_transform_feedback_varyings; | 
|  | memcpy(entries, &varyings[0], entry_size); | 
|  |  | 
|  | for (uint32_t ii = 0; ii < num_transform_feedback_varyings; ++ii) { | 
|  | memcpy(data, names[ii].c_str(), names[ii].length() + 1); | 
|  | data += names[ii].length() + 1; | 
|  | } | 
|  | DCHECK_EQ(ComputeOffset(header, data), total_size); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::GetUniformsES3(CommonDecoder::Bucket* bucket) const { | 
|  | // The data is packed into the bucket in the following order | 
|  | //   1) header | 
|  | //   2) N entries of UniformES3Info | 
|  | // | 
|  | // We query all the data directly through GL calls, assuming they are | 
|  | // cheap through MANGLE. | 
|  |  | 
|  | DCHECK(bucket); | 
|  | GLuint program = service_id(); | 
|  |  | 
|  | uint32_t header_size = sizeof(UniformsES3Header); | 
|  | bucket->SetSize(header_size);  // In case we fail. | 
|  |  | 
|  | GLsizei count = 0; | 
|  | GLint param = GL_FALSE; | 
|  | // We assume program is a valid program service id. | 
|  | glGetProgramiv(program, GL_LINK_STATUS, ¶m); | 
|  | if (param == GL_TRUE) { | 
|  | param = 0; | 
|  | glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count); | 
|  | } | 
|  | if (count == 0) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | base::CheckedNumeric<uint32_t> size = sizeof(UniformES3Info); | 
|  | size *= count; | 
|  | uint32_t entry_size = size.ValueOrDefault(0); | 
|  | size += header_size; | 
|  | if (!size.IsValid()) | 
|  | return false; | 
|  | uint32_t total_size = size.ValueOrDefault(0); | 
|  | bucket->SetSize(total_size); | 
|  | UniformsES3Header* header = | 
|  | bucket->GetDataAs<UniformsES3Header*>(0, header_size); | 
|  | DCHECK(header); | 
|  | header->num_uniforms = static_cast<uint32_t>(count); | 
|  |  | 
|  | // Instead of GetDataAs<UniformES3Info*>, we do GetDataAs<int32_t>. This is | 
|  | // because struct UniformES3Info is defined as five int32_t. | 
|  | // By doing this, we can fill the structs through loops. | 
|  | int32_t* entries = | 
|  | bucket->GetDataAs<int32_t*>(header_size, entry_size); | 
|  | DCHECK(entries); | 
|  | const size_t kStride = sizeof(UniformES3Info) / sizeof(int32_t); | 
|  |  | 
|  | const GLenum kPname[] = { | 
|  | GL_UNIFORM_BLOCK_INDEX, | 
|  | GL_UNIFORM_OFFSET, | 
|  | GL_UNIFORM_ARRAY_STRIDE, | 
|  | GL_UNIFORM_MATRIX_STRIDE, | 
|  | GL_UNIFORM_IS_ROW_MAJOR, | 
|  | }; | 
|  | const GLint kDefaultValue[] = { -1, -1, -1, -1, 0 }; | 
|  | const size_t kNumPnames = arraysize(kPname); | 
|  | std::vector<GLuint> indices(count); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | indices[ii] = ii; | 
|  | } | 
|  | std::vector<GLint> params(count); | 
|  | for (size_t pname_index = 0; pname_index < kNumPnames; ++pname_index) { | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | params[ii] = kDefaultValue[pname_index]; | 
|  | } | 
|  | glGetActiveUniformsiv( | 
|  | program, count, &indices[0], kPname[pname_index], ¶ms[0]); | 
|  | for (GLsizei ii = 0; ii < count; ++ii) { | 
|  | entries[kStride * ii + pname_index] = params[ii]; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const Program::ProgramOutputInfo* Program::GetProgramOutputInfo( | 
|  | const std::string& name) const { | 
|  | for (const auto& info : program_output_infos_) { | 
|  | if (info.name == name) { | 
|  | return &info; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | GLint Program::GetFragDataLocation(const std::string& original_name) const { | 
|  | DCHECK(IsValid()); | 
|  | const ProgramOutputInfo* info = GetProgramOutputInfo(original_name); | 
|  | if (!info) | 
|  | info = GetProgramOutputInfo(original_name + "[0]"); | 
|  | if (!info) | 
|  | return -1; | 
|  | return info->color_name; | 
|  | } | 
|  |  | 
|  | GLint Program::GetFragDataIndex(const std::string& original_name) const { | 
|  | DCHECK(IsValid()); | 
|  | const ProgramOutputInfo* info = GetProgramOutputInfo(original_name); | 
|  | if (!info) | 
|  | info = GetProgramOutputInfo(original_name + "[0]"); | 
|  | if (!info) | 
|  | return -1; | 
|  | return info->index; | 
|  | } | 
|  |  | 
|  | void Program::TransformFeedbackVaryings(GLsizei count, | 
|  | const char* const* varyings, | 
|  | GLenum buffer_mode) { | 
|  | transform_feedback_varyings_.clear(); | 
|  | for (GLsizei i = 0; i < count; ++i) { | 
|  | transform_feedback_varyings_.push_back(std::string(varyings[i])); | 
|  | } | 
|  | transform_feedback_buffer_mode_ = buffer_mode; | 
|  | } | 
|  |  | 
|  | Program::~Program() { | 
|  | if (manager_) { | 
|  | if (manager_->have_context_) { | 
|  | glDeleteProgram(service_id()); | 
|  | } | 
|  | manager_->StopTracking(this); | 
|  | manager_ = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | ProgramManager::ProgramManager( | 
|  | ProgramCache* program_cache, | 
|  | uint32_t max_varying_vectors, | 
|  | uint32_t max_draw_buffers, | 
|  | uint32_t max_dual_source_draw_buffers, | 
|  | uint32_t max_vertex_attribs, | 
|  | const GpuPreferences& gpu_preferences, | 
|  | FeatureInfo* feature_info) | 
|  | : program_count_(0), | 
|  | have_context_(true), | 
|  | program_cache_(program_cache), | 
|  | max_varying_vectors_(max_varying_vectors), | 
|  | max_draw_buffers_(max_draw_buffers), | 
|  | max_dual_source_draw_buffers_(max_dual_source_draw_buffers), | 
|  | max_vertex_attribs_(max_vertex_attribs), | 
|  | gpu_preferences_(gpu_preferences), | 
|  | feature_info_(feature_info) {} | 
|  |  | 
|  | ProgramManager::~ProgramManager() { | 
|  | DCHECK(programs_.empty()); | 
|  | } | 
|  |  | 
|  | void ProgramManager::Destroy(bool have_context) { | 
|  | have_context_ = have_context; | 
|  |  | 
|  | ProgramDeletionScopedUmaTimeAndRate scoped_histogram( | 
|  | base::saturated_cast<int32_t>(programs_.size())); | 
|  | programs_.clear(); | 
|  | } | 
|  |  | 
|  | void ProgramManager::StartTracking(Program* /* program */) { | 
|  | ++program_count_; | 
|  | } | 
|  |  | 
|  | void ProgramManager::StopTracking(Program* /* program */) { | 
|  | --program_count_; | 
|  | } | 
|  |  | 
|  | Program* ProgramManager::CreateProgram( | 
|  | GLuint client_id, GLuint service_id) { | 
|  | std::pair<ProgramMap::iterator, bool> result = | 
|  | programs_.insert( | 
|  | std::make_pair(client_id, | 
|  | scoped_refptr<Program>( | 
|  | new Program(this, service_id)))); | 
|  | DCHECK(result.second); | 
|  | return result.first->second.get(); | 
|  | } | 
|  |  | 
|  | Program* ProgramManager::GetProgram(GLuint client_id) { | 
|  | ProgramMap::iterator it = programs_.find(client_id); | 
|  | return it != programs_.end() ? it->second.get() : NULL; | 
|  | } | 
|  |  | 
|  | bool ProgramManager::GetClientId(GLuint service_id, GLuint* client_id) const { | 
|  | // This doesn't need to be fast. It's only used during slow queries. | 
|  | for (const auto& key_value : programs_) { | 
|  | if (key_value.second->service_id() == service_id) { | 
|  | *client_id = key_value.first; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ProgramCache* ProgramManager::program_cache() const { | 
|  | return program_cache_; | 
|  | } | 
|  |  | 
|  | bool ProgramManager::IsOwned(Program* program) const { | 
|  | for (const auto& key_value : programs_) { | 
|  | if (key_value.second.get() == program) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void ProgramManager::RemoveProgramInfoIfUnused( | 
|  | ShaderManager* shader_manager, Program* program) { | 
|  | DCHECK(shader_manager); | 
|  | DCHECK(program); | 
|  | DCHECK(IsOwned(program)); | 
|  | if (program->IsDeleted() && !program->InUse()) { | 
|  | program->DetachShaders(shader_manager); | 
|  | for (ProgramMap::iterator it = programs_.begin(); | 
|  | it != programs_.end(); ++it) { | 
|  | if (it->second.get() == program) { | 
|  | programs_.erase(it); | 
|  | return; | 
|  | } | 
|  | } | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ProgramManager::MarkAsDeleted( | 
|  | ShaderManager* shader_manager, | 
|  | Program* program) { | 
|  | DCHECK(shader_manager); | 
|  | DCHECK(program); | 
|  | DCHECK(IsOwned(program)); | 
|  | program->MarkAsDeleted(); | 
|  | RemoveProgramInfoIfUnused(shader_manager, program); | 
|  | } | 
|  |  | 
|  | void ProgramManager::UseProgram(Program* program) { | 
|  | DCHECK(program); | 
|  | DCHECK(IsOwned(program)); | 
|  | program->IncUseCount(); | 
|  | } | 
|  |  | 
|  | void ProgramManager::UnuseProgram( | 
|  | ShaderManager* shader_manager, | 
|  | Program* program) { | 
|  | DCHECK(shader_manager); | 
|  | DCHECK(program); | 
|  | DCHECK(IsOwned(program)); | 
|  | program->DecUseCount(); | 
|  | RemoveProgramInfoIfUnused(shader_manager, program); | 
|  | } | 
|  |  | 
|  | void ProgramManager::ClearUniforms(Program* program) { | 
|  | DCHECK(program); | 
|  | program->ClearUniforms(&zero_); | 
|  | } | 
|  |  | 
|  | int32_t ProgramManager::MakeFakeLocation(int32_t index, int32_t element) { | 
|  | return index + element * 0x10000; | 
|  | } | 
|  |  | 
|  | }  // namespace gles2 | 
|  | }  // namespace gpu |