summaryrefslogtreecommitdiff
path: root/chromium/gpu/command_buffer/service/program_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/gpu/command_buffer/service/program_manager.cc')
-rw-r--r--chromium/gpu/command_buffer/service/program_manager.cc1187
1 files changed, 1187 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/service/program_manager.cc b/chromium/gpu/command_buffer/service/program_manager.cc
new file mode 100644
index 00000000000..8b1eedea35e
--- /dev/null
+++ b/chromium/gpu/command_buffer/service/program_manager.cc
@@ -0,0 +1,1187 @@
+// 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 <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_number_conversions.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_switches.h"
+#include "gpu/command_buffer/service/program_cache.h"
+#include "gpu/command_buffer/service/shader_manager.h"
+#include "gpu/command_buffer/service/shader_translator.h"
+#include "third_party/re2/re2/re2.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;
+ }
+}
+
+ShaderTranslator* ShaderIndexToTranslator(
+ int index,
+ ShaderTranslator* vertex_translator,
+ ShaderTranslator* fragment_translator) {
+ switch (index) {
+ case 0:
+ return vertex_translator;
+ case 1:
+ return fragment_translator;
+ default:
+ NOTREACHED();
+ return NULL;
+ }
+}
+
+// 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[name.size() - 1] != ']') {
+ *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;
+ }
+
+ GLint index = 0;
+ size_t last = name.size() - 1;
+ for (size_t pos = open_pos + 1; pos < last; ++pos) {
+ int8 digit = name[pos] - '0';
+ if (digit < 0 || digit > 9) {
+ return false;
+ }
+ index = index * 10 + digit;
+ }
+
+ *element_index = index;
+ *new_name = name.substr(0, open_pos);
+ return true;
+}
+
+} // anonymous namespace.
+
+Program::UniformInfo::UniformInfo()
+ : size(0),
+ type(GL_NONE),
+ fake_location_base(0),
+ is_array(false) {
+}
+
+Program::UniformInfo::UniformInfo(
+ GLsizei _size,
+ GLenum _type,
+ int _fake_location_base,
+ const std::string& _name)
+ : size(_size),
+ type(_type),
+ fake_location_base(_fake_location_base),
+ is_array(false),
+ name(_name) {
+}
+
+Program::UniformInfo::~UniformInfo() {}
+
+bool ProgramManager::IsInvalidPrefix(const char* name, size_t length) {
+ static const char kInvalidPrefix[] = { 'g', 'l', '_' };
+ return (length >= sizeof(kInvalidPrefix) &&
+ memcmp(name, kInvalidPrefix, sizeof(kInvalidPrefix)) == 0);
+}
+
+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),
+ num_uniforms_(0) {
+ manager_->StartTracking(this);
+}
+
+void Program::Reset() {
+ valid_ = false;
+ link_status_ = false;
+ num_uniforms_ = 0;
+ max_uniform_name_length_ = 0;
+ max_attrib_name_length_ = 0;
+ attrib_infos_.clear();
+ uniform_infos_.clear();
+ sampler_indices_.clear();
+ attrib_location_to_index_map_.clear();
+}
+
+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(NULL);
+ return;
+ }
+ scoped_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);
+ set_log_info(ProcessLogInfo(log).c_str());
+}
+
+void Program::ClearUniforms(
+ std::vector<uint8>* zero_buffer) {
+ DCHECK(zero_buffer);
+ if (uniforms_cleared_) {
+ return;
+ }
+ uniforms_cleared_ = true;
+ for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
+ const UniformInfo& uniform_info = uniform_infos_[ii];
+ if (!uniform_info.IsValid()) {
+ continue;
+ }
+ GLint location = uniform_info.element_locations[0];
+ GLsizei size = uniform_info.size;
+ uint32 unit_size = GLES2Util::GetGLDataTypeSizeForUniforms(
+ uniform_info.type);
+ uint32 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:
+ case GL_SAMPLER_3D_OES:
+ case GL_SAMPLER_2D_RECT_ARB:
+ 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;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+namespace {
+
+struct UniformData {
+ UniformData() : size(-1), type(GL_NONE), location(0), added(false) {
+ }
+ std::string queried_name;
+ std::string corrected_name;
+ std::string original_name;
+ GLsizei size;
+ GLenum type;
+ GLint location;
+ bool added;
+};
+
+struct UniformDataComparer {
+ bool operator()(const UniformData& lhs, const UniformData& rhs) const {
+ return lhs.queried_name < rhs.queried_name;
+ }
+};
+
+} // anonymous namespace
+
+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?
+ scoped_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');
+ if (!ProgramManager::IsInvalidPrefix(name_buffer.get(), length)) {
+ std::string name;
+ std::string original_name;
+ GetCorrectedVariableInfo(
+ false, name_buffer.get(), &name, &original_name, &size, &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(size, 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];
+ attrib_location_to_index_map_[info.location] = ii;
+ }
+
+#if !defined(NDEBUG)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableGPUServiceLoggingGPU)) {
+ DLOG(INFO) << "----: attribs for service_id: " << service_id();
+ for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) {
+ const VertexAttrib& info = attrib_infos_[ii];
+ DLOG(INFO) << ii << ": loc = " << info.location
+ << ", size = " << info.size
+ << ", type = " << GLES2Util::GetStringEnum(info.type)
+ << ", name = " << info.name;
+ }
+ }
+#endif
+
+ max_len = 0;
+ GLint num_uniforms = 0;
+ glGetProgramiv(service_id_, GL_ACTIVE_UNIFORMS, &num_uniforms);
+ glGetProgramiv(service_id_, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_len);
+ name_buffer.reset(new char[max_len]);
+
+ // Reads all the names.
+ std::vector<UniformData> uniform_data_;
+ for (GLint ii = 0; ii < num_uniforms; ++ii) {
+ GLsizei length = 0;
+ UniformData data;
+ glGetActiveUniform(
+ service_id_, ii, max_len, &length,
+ &data.size, &data.type, name_buffer.get());
+ DCHECK(max_len == 0 || length < max_len);
+ DCHECK(length == 0 || name_buffer[length] == '\0');
+ if (!ProgramManager::IsInvalidPrefix(name_buffer.get(), length)) {
+ data.queried_name = std::string(name_buffer.get());
+ GetCorrectedVariableInfo(
+ true, name_buffer.get(), &data.corrected_name, &data.original_name,
+ &data.size, &data.type);
+ uniform_data_.push_back(data);
+ }
+ }
+
+ // NOTE: We don't care if 2 uniforms are bound to the same location.
+ // One of them will take preference. The spec allows this, same as
+ // BindAttribLocation.
+ //
+ // The reason we don't check is if we were to fail we'd have to
+ // restore the previous program but since we've already linked successfully
+ // at this point the previous program is gone.
+
+ // Assigns the uniforms with bindings.
+ size_t next_available_index = 0;
+ for (size_t ii = 0; ii < uniform_data_.size(); ++ii) {
+ UniformData& data = uniform_data_[ii];
+ data.location = glGetUniformLocation(
+ service_id_, data.queried_name.c_str());
+ // remove "[0]"
+ std::string short_name;
+ int element_index = 0;
+ bool good ALLOW_UNUSED = GetUniformNameSansElement(
+ data.queried_name, &element_index, &short_name);\
+ DCHECK(good);
+ LocationMap::const_iterator it = bind_uniform_location_map_.find(
+ short_name);
+ if (it != bind_uniform_location_map_.end()) {
+ data.added = AddUniformInfo(
+ data.size, data.type, data.location, it->second, data.corrected_name,
+ data.original_name, &next_available_index);
+ }
+ }
+
+ // Assigns the uniforms that were not bound.
+ for (size_t ii = 0; ii < uniform_data_.size(); ++ii) {
+ const UniformData& data = uniform_data_[ii];
+ if (!data.added) {
+ AddUniformInfo(
+ data.size, data.type, data.location, -1, data.corrected_name,
+ data.original_name, &next_available_index);
+ }
+ }
+
+#if !defined(NDEBUG)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableGPUServiceLoggingGPU)) {
+ DLOG(INFO) << "----: uniforms for service_id: " << service_id();
+ for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
+ const UniformInfo& info = uniform_infos_[ii];
+ if (info.IsValid()) {
+ DLOG(INFO) << ii << ": loc = " << info.element_locations[0]
+ << ", size = " << info.size
+ << ", type = " << GLES2Util::GetStringEnum(info.type)
+ << ", name = " << info.name;
+ }
+ }
+ }
+#endif
+
+ valid_ = true;
+}
+
+void Program::ExecuteBindAttribLocationCalls() {
+ for (LocationMap::const_iterator it = bind_attrib_location_map_.begin();
+ it != bind_attrib_location_map_.end(); ++it) {
+ const std::string* mapped_name = GetAttribMappedName(it->first);
+ if (mapped_name && *mapped_name != it->first)
+ glBindAttribLocation(service_id_, it->second, mapped_name->c_str());
+ }
+}
+
+void ProgramManager::DoCompileShader(Shader* shader,
+ ShaderTranslator* translator,
+ FeatureInfo* feature_info) {
+ // Translate GL ES 2.0 shader to Desktop GL shader and pass that to
+ // glShaderSource and then glCompileShader.
+ const std::string* source = shader->source();
+ const char* shader_src = source ? source->c_str() : "";
+ if (translator) {
+ if (!translator->Translate(shader_src)) {
+ shader->SetStatus(false, translator->info_log(), NULL);
+ return;
+ }
+ shader_src = translator->translated_shader();
+ if (!feature_info->feature_flags().angle_translated_shader_source)
+ shader->UpdateTranslatedSource(shader_src);
+ }
+
+ glShaderSource(shader->service_id(), 1, &shader_src, NULL);
+ glCompileShader(shader->service_id());
+ if (feature_info->feature_flags().angle_translated_shader_source) {
+ GLint max_len = 0;
+ glGetShaderiv(shader->service_id(),
+ GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE,
+ &max_len);
+ scoped_ptr<char[]> temp(new char[max_len]);
+ GLint len = 0;
+ glGetTranslatedShaderSourceANGLE(
+ shader->service_id(), max_len, &len, temp.get());
+ DCHECK(max_len == 0 || len < max_len);
+ DCHECK(len == 0 || temp[len] == '\0');
+ shader->UpdateTranslatedSource(max_len ? temp.get() : NULL);
+ }
+
+ GLint status = GL_FALSE;
+ glGetShaderiv(shader->service_id(), GL_COMPILE_STATUS, &status);
+ if (status) {
+ shader->SetStatus(true, "", translator);
+ } else {
+ // We cannot reach here if we are using the shader translator.
+ // All invalid shaders must be rejected by the translator.
+ // All translated shaders must compile.
+ GLint max_len = 0;
+ glGetShaderiv(shader->service_id(), GL_INFO_LOG_LENGTH, &max_len);
+ scoped_ptr<char[]> temp(new char[max_len]);
+ GLint len = 0;
+ glGetShaderInfoLog(shader->service_id(), max_len, &len, temp.get());
+ DCHECK(max_len == 0 || len < max_len);
+ DCHECK(len == 0 || temp[len] == '\0');
+ shader->SetStatus(false, std::string(temp.get(), len).c_str(), NULL);
+ LOG_IF(ERROR, translator)
+ << "Shader translator allowed/produced an invalid shader "
+ << "unless the driver is buggy:"
+ << "\n--original-shader--\n" << (source ? *source : std::string())
+ << "\n--translated-shader--\n" << shader_src << "\n--info-log--\n"
+ << *shader->log_info();
+ }
+}
+
+bool Program::Link(ShaderManager* manager,
+ ShaderTranslator* vertex_translator,
+ ShaderTranslator* fragment_translator,
+ FeatureInfo* feature_info,
+ const ShaderCacheCallback& shader_callback) {
+ ClearLinkStatus();
+ if (!CanLink()) {
+ set_log_info("missing shaders");
+ return false;
+ }
+ if (DetectAttribLocationBindingConflicts()) {
+ set_log_info("glBindAttribLocation() conflicts");
+ return false;
+ }
+
+ TimeTicks before_time = TimeTicks::HighResNow();
+ bool link = true;
+ ProgramCache* cache = manager_->program_cache_;
+ if (cache) {
+ DCHECK(attached_shaders_[0]->signature_source() &&
+ attached_shaders_[1]->signature_source());
+ ProgramCache::LinkedProgramStatus status = cache->GetLinkedProgramStatus(
+ *attached_shaders_[0]->signature_source(),
+ vertex_translator,
+ *attached_shaders_[1]->signature_source(),
+ fragment_translator,
+ &bind_attrib_location_map_);
+
+ if (status == ProgramCache::LINK_SUCCEEDED) {
+ ProgramCache::ProgramLoadResult success =
+ cache->LoadLinkedProgram(service_id(),
+ attached_shaders_[0].get(),
+ vertex_translator,
+ attached_shaders_[1].get(),
+ fragment_translator,
+ &bind_attrib_location_map_,
+ shader_callback);
+ link = success != ProgramCache::PROGRAM_LOAD_SUCCESS;
+ UMA_HISTOGRAM_BOOLEAN("GPU.ProgramCache.LoadBinarySuccess", !link);
+ }
+ }
+
+ if (link) {
+ ExecuteBindAttribLocationCalls();
+ before_time = TimeTicks::HighResNow();
+ if (cache && gfx::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) {
+ if (cache) {
+ cache->SaveLinkedProgram(service_id(),
+ attached_shaders_[0].get(),
+ vertex_translator,
+ attached_shaders_[1].get(),
+ fragment_translator,
+ &bind_attrib_location_map_,
+ shader_callback);
+ }
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "GPU.ProgramCache.BinaryCacheMissTime",
+ (TimeTicks::HighResNow() - before_time).InMicroseconds(),
+ 0,
+ TimeDelta::FromSeconds(10).InMicroseconds(),
+ 50);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "GPU.ProgramCache.BinaryCacheHitTime",
+ (TimeTicks::HighResNow() - before_time).InMicroseconds(),
+ 0,
+ 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 {
+ bool getting_array_location = false;
+ size_t open_pos = std::string::npos;
+ int index = 0;
+ if (!GLES2Util::ParseUniformName(
+ name, &open_pos, &index, &getting_array_location)) {
+ return -1;
+ }
+ for (GLuint ii = 0; ii < uniform_infos_.size(); ++ii) {
+ const UniformInfo& info = uniform_infos_[ii];
+ if (!info.IsValid()) {
+ continue;
+ }
+ if (info.name == name ||
+ (info.is_array &&
+ info.name.compare(0, info.name.size() - 3, name) == 0)) {
+ return info.fake_location_base;
+ } else if (getting_array_location && info.is_array) {
+ // Look for an array specification.
+ size_t open_pos_2 = info.name.find_last_of('[');
+ if (open_pos_2 == open_pos &&
+ name.compare(0, open_pos, info.name, 0, open_pos) == 0) {
+ if (index >= 0 && index < info.size) {
+ return ProgramManager::MakeFakeLocation(
+ info.fake_location_base, index);
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+GLint Program::GetAttribLocation(
+ const std::string& name) const {
+ for (GLuint ii = 0; ii < attrib_infos_.size(); ++ii) {
+ const VertexAttrib& info = attrib_infos_[ii];
+ if (info.name == name) {
+ return info.location;
+ }
+ }
+ return -1;
+}
+
+const 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 NULL;
+ }
+
+ GLint uniform_index = GetUniformInfoIndexFromFakeLocation(fake_location);
+ if (uniform_index >= 0 &&
+ static_cast<size_t>(uniform_index) < uniform_infos_.size()) {
+ const UniformInfo& uniform_info = uniform_infos_[uniform_index];
+ if (!uniform_info.IsValid()) {
+ return NULL;
+ }
+ GLint element_index = GetArrayElementIndexFromFakeLocation(fake_location);
+ if (element_index < uniform_info.size) {
+ *real_location = uniform_info.element_locations[element_index];
+ *array_index = element_index;
+ return &uniform_info;
+ }
+ }
+ return NULL;
+}
+
+const std::string* Program::GetAttribMappedName(
+ const std::string& original_name) const {
+ for (int ii = 0; ii < kMaxAttachedShaders; ++ii) {
+ Shader* shader = attached_shaders_[ii].get();
+ if (shader) {
+ const std::string* mapped_name =
+ shader->GetAttribMappedName(original_name);
+ if (mapped_name)
+ return mapped_name;
+ }
+ }
+ return NULL;
+}
+
+const std::string* Program::GetOriginalNameFromHashedName(
+ const std::string& hashed_name) const {
+ for (int ii = 0; ii < kMaxAttachedShaders; ++ii) {
+ Shader* shader = attached_shaders_[ii].get();
+ if (shader) {
+ const std::string* original_name =
+ shader->GetOriginalNameFromHashedName(hashed_name);
+ if (original_name)
+ return original_name;
+ }
+ }
+ return NULL;
+}
+
+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;
+}
+
+// Note: This is only valid to call right after a program has been linked
+// successfully.
+void Program::GetCorrectedVariableInfo(
+ bool use_uniforms,
+ const std::string& name, std::string* corrected_name,
+ std::string* original_name,
+ GLsizei* size, GLenum* type) const {
+ DCHECK(corrected_name);
+ DCHECK(original_name);
+ DCHECK(size);
+ DCHECK(type);
+ const char* kArraySpec = "[0]";
+ for (int jj = 0; jj < 2; ++jj) {
+ std::string test_name(name + ((jj == 1) ? kArraySpec : ""));
+ for (int ii = 0; ii < kMaxAttachedShaders; ++ii) {
+ Shader* shader = attached_shaders_[ii].get();
+ if (shader) {
+ const Shader::VariableInfo* variable_info =
+ use_uniforms ? shader->GetUniformInfo(test_name) :
+ shader->GetAttribInfo(test_name);
+ // Note: There is an assuption here that if an attrib is defined in more
+ // than 1 attached shader their types and sizes match. Should we check
+ // for that case?
+ if (variable_info) {
+ *corrected_name = test_name;
+ *original_name = variable_info->name;
+ *type = variable_info->type;
+ *size = variable_info->size;
+ return;
+ }
+ }
+ }
+ }
+ *corrected_name = name;
+ *original_name = name;
+}
+
+bool Program::AddUniformInfo(
+ GLsizei size, GLenum type, GLint location, GLint fake_base_location,
+ const std::string& name, const std::string& original_name,
+ size_t* next_available_index) {
+ DCHECK(next_available_index);
+ const char* kArraySpec = "[0]";
+ size_t uniform_index =
+ fake_base_location >= 0 ? fake_base_location : *next_available_index;
+ if (uniform_infos_.size() < uniform_index + 1) {
+ uniform_infos_.resize(uniform_index + 1);
+ }
+
+ // return if this location is already in use.
+ if (uniform_infos_[uniform_index].IsValid()) {
+ DCHECK_GE(fake_base_location, 0);
+ return false;
+ }
+
+ uniform_infos_[uniform_index] = UniformInfo(
+ size, type, uniform_index, original_name);
+ ++num_uniforms_;
+
+ UniformInfo& info = uniform_infos_[uniform_index];
+ info.element_locations.resize(size);
+ info.element_locations[0] = location;
+ DCHECK_GE(size, 0);
+ size_t num_texture_units = info.IsSampler() ? static_cast<size_t>(size) : 0u;
+ info.texture_units.clear();
+ info.texture_units.resize(num_texture_units, 0);
+
+ if (size > 1) {
+ // Go through the array element locations looking for a match.
+ // We can skip the first element because it's the same as the
+ // the location without the array operators.
+ size_t array_pos = name.rfind(kArraySpec);
+ std::string base_name = name;
+ if (name.size() > 3) {
+ if (array_pos != name.size() - 3) {
+ info.name = name + kArraySpec;
+ } else {
+ base_name = name.substr(0, name.size() - 3);
+ }
+ }
+ for (GLsizei ii = 1; ii < info.size; ++ii) {
+ std::string element_name(base_name + "[" + base::IntToString(ii) + "]");
+ info.element_locations[ii] =
+ glGetUniformLocation(service_id_, element_name.c_str());
+ }
+ }
+
+ info.is_array =
+ (size > 1 ||
+ (info.name.size() > 3 &&
+ info.name.rfind(kArraySpec) == info.name.size() - 3));
+
+ if (info.IsSampler()) {
+ sampler_indices_.push_back(info.fake_location_base);
+ }
+ max_uniform_name_length_ =
+ std::max(max_uniform_name_length_,
+ static_cast<GLsizei>(info.name.size()));
+
+ while (*next_available_index < uniform_infos_.size() &&
+ uniform_infos_[*next_available_index].IsValid()) {
+ *next_available_index = *next_available_index + 1;
+ }
+
+ return true;
+}
+
+const Program::UniformInfo*
+ Program::GetUniformInfo(
+ GLint index) const {
+ if (static_cast<size_t>(index) >= uniform_infos_.size()) {
+ return NULL;
+ }
+
+ const UniformInfo& info = uniform_infos_[index];
+ return info.IsValid() ? &info : NULL;
+}
+
+bool Program::SetSamplers(
+ GLint num_texture_units, GLint fake_location,
+ GLsizei count, const GLint* value) {
+ if (fake_location < 0) {
+ return true;
+ }
+ GLint uniform_index = GetUniformInfoIndexFromFakeLocation(fake_location);
+ if (uniform_index >= 0 &&
+ static_cast<size_t>(uniform_index) < uniform_infos_.size()) {
+ UniformInfo& info = uniform_infos_[uniform_index];
+ if (!info.IsValid()) {
+ return false;
+ }
+ GLint element_index = GetArrayElementIndexFromFakeLocation(fake_location);
+ if (element_index < info.size) {
+ count = std::min(info.size - 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 = num_uniforms_;
+ 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::DetachShader(
+ ShaderManager* shader_manager,
+ Shader* shader) {
+ DCHECK(shader_manager);
+ DCHECK(shader);
+ if (attached_shaders_[ShaderTypeToIndex(shader->shader_type())].get() !=
+ shader) {
+ return false;
+ }
+ attached_shaders_[ShaderTypeToIndex(shader->shader_type())] = NULL;
+ shader_manager->UnuseShader(shader);
+ return true;
+}
+
+void Program::DetachShaders(ShaderManager* shader_manager) {
+ DCHECK(shader_manager);
+ for (int ii = 0; ii < kMaxAttachedShaders; ++ii) {
+ if (attached_shaders_[ii].get()) {
+ DetachShader(shader_manager, attached_shaders_[ii].get());
+ }
+ }
+}
+
+bool Program::CanLink() const {
+ for (int ii = 0; ii < kMaxAttachedShaders; ++ii) {
+ if (!attached_shaders_[ii].get() || !attached_shaders_[ii]->IsValid()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Program::DetectAttribLocationBindingConflicts() const {
+ std::set<GLint> location_binding_used;
+ for (LocationMap::const_iterator it = bind_attrib_location_map_.begin();
+ it != bind_attrib_location_map_.end(); ++it) {
+ // Find out if an attribute is declared in this program's shaders.
+ bool active = false;
+ for (int ii = 0; ii < kMaxAttachedShaders; ++ii) {
+ if (!attached_shaders_[ii].get() || !attached_shaders_[ii]->IsValid())
+ continue;
+ if (attached_shaders_[ii]->GetAttribInfo(it->first)) {
+ active = true;
+ break;
+ }
+ }
+ if (active) {
+ std::pair<std::set<GLint>::iterator, bool> result =
+ location_binding_used.insert(it->second);
+ if (!result.second)
+ return true;
+ }
+ }
+ return false;
+}
+
+static uint32 ComputeOffset(const void* start, const void* position) {
+ return static_cast<const uint8*>(position) -
+ static_cast<const uint8*>(start);
+}
+
+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 num_locations = 0;
+ uint32 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 (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
+ const UniformInfo& info = uniform_infos_[ii];
+ if (info.IsValid()) {
+ num_locations += info.element_locations.size();
+ total_string_size += info.name.size();
+ }
+ }
+
+ uint32 num_inputs = attrib_infos_.size() + num_uniforms_;
+ uint32 input_size = num_inputs * sizeof(ProgramInput);
+ uint32 location_size = num_locations * sizeof(int32);
+ uint32 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* locations = bucket->GetDataAs<int32*>(
+ 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 = num_uniforms_;
+
+ 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 (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
+ const UniformInfo& info = uniform_infos_[ii];
+ if (info.IsValid()) {
+ 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) {
+ *locations++ = ProgramManager::MakeFakeLocation(ii, jj);
+ }
+ memcpy(strings, info.name.c_str(), info.name.size());
+ strings += info.name.size();
+ ++inputs;
+ }
+ }
+
+ DCHECK_EQ(ComputeOffset(header, strings), size);
+}
+
+Program::~Program() {
+ if (manager_) {
+ if (manager_->have_context_) {
+ glDeleteProgram(service_id());
+ }
+ manager_->StopTracking(this);
+ manager_ = NULL;
+ }
+}
+
+
+ProgramManager::ProgramManager(ProgramCache* program_cache)
+ : program_count_(0),
+ have_context_(true),
+ disable_workarounds_(
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableGpuDriverBugWorkarounds)),
+ program_cache_(program_cache) { }
+
+ProgramManager::~ProgramManager() {
+ DCHECK(programs_.empty());
+}
+
+void ProgramManager::Destroy(bool have_context) {
+ have_context_ = have_context;
+ 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 (ProgramMap::const_iterator it = programs_.begin();
+ it != programs_.end(); ++it) {
+ if (it->second->service_id() == service_id) {
+ *client_id = it->first;
+ return true;
+ }
+ }
+ return false;
+}
+
+ProgramCache* ProgramManager::program_cache() const {
+ return program_cache_;
+}
+
+bool ProgramManager::IsOwned(Program* program) {
+ for (ProgramMap::iterator it = programs_.begin();
+ it != programs_.end(); ++it) {
+ if (it->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();
+ ClearUniforms(program);
+}
+
+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);
+ if (!disable_workarounds_) {
+ program->ClearUniforms(&zero_);
+ }
+}
+
+int32 ProgramManager::MakeFakeLocation(int32 index, int32 element) {
+ return index + element * 0x10000;
+}
+
+} // namespace gles2
+} // namespace gpu