Index: gpu/command_buffer/service/program_manager.cc |
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc |
index 0eb90a332a6c1d838380186cf2cabcd14580d155..8b94e8ba77920c17303e4aa6c6e1449b96f97be8 100644 |
--- a/gpu/command_buffer/service/program_manager.cc |
+++ b/gpu/command_buffer/service/program_manager.cc |
@@ -4,7 +4,6 @@ |
#include "gpu/command_buffer/service/program_manager.h" |
-#include <algorithm> |
#include <set> |
#include <vector> |
@@ -21,7 +20,9 @@ |
namespace gpu { |
namespace gles2 { |
-static int ShaderTypeToIndex(GLenum shader_type) { |
+namespace { |
+ |
+int ShaderTypeToIndex(GLenum shader_type) { |
switch (shader_type) { |
case GL_VERTEX_SHADER: |
return 0; |
@@ -33,6 +34,51 @@ static int ShaderTypeToIndex(GLenum shader_type) { |
} |
} |
+// 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; |
+ } |
+ |
+ if (index < 0) { |
+ return false; |
+ } |
+ |
+ *element_index = index; |
+ *new_name = name.substr(0, open_pos); |
+ return true; |
+} |
+ |
+} // anonymous namespace. |
+ |
+ProgramManager::ProgramInfo::UniformInfo::UniformInfo() |
+ : size(0) { |
+} |
+ |
ProgramManager::ProgramInfo::UniformInfo::UniformInfo( |
GLsizei _size, |
GLenum _type, |
@@ -63,13 +109,15 @@ ProgramManager::ProgramInfo::ProgramInfo( |
deleted_(false), |
valid_(false), |
link_status_(false), |
- uniforms_cleared_(false) { |
+ uniforms_cleared_(false), |
+ num_uniforms_(0) { |
manager_->StartTracking(this); |
} |
void ProgramManager::ProgramInfo::Reset() { |
valid_ = false; |
link_status_ = false; |
+ num_uniforms_ = 0; |
max_uniform_name_length_ = 0; |
max_attrib_name_length_ = 0; |
attrib_infos_.clear(); |
@@ -102,6 +150,9 @@ void ProgramManager::ProgramInfo::ClearUniforms( |
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( |
@@ -167,11 +218,15 @@ void ProgramManager::ProgramInfo::ClearUniforms( |
namespace { |
struct UniformData { |
+ UniformData() : size(-1), type(GL_NONE), added(false) { |
+ } |
std::string queried_name; |
std::string corrected_name; |
std::string original_name; |
GLsizei size; |
GLenum type; |
+ GLint location; |
+ bool added; |
}; |
struct UniformDataComparer { |
@@ -235,7 +290,7 @@ void ProgramManager::ProgramInfo::Update() { |
glGetProgramiv(service_id_, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_len); |
name_buffer.reset(new char[max_len]); |
- // Read all the names first and sort them so we get a consistent list |
+ // Reads all the names. |
std::vector<UniformData> uniform_data_; |
for (GLint ii = 0; ii < num_uniforms; ++ii) { |
GLsizei length = 0; |
@@ -254,28 +309,50 @@ void ProgramManager::ProgramInfo::Update() { |
} |
} |
- std::sort(uniform_data_.begin(), uniform_data_.end(), UniformDataComparer()); |
+ // 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) { |
- const UniformData& data = uniform_data_[ii]; |
- GLint location = glGetUniformLocation( |
+ UniformData& data = uniform_data_[ii]; |
+ data.location = glGetUniformLocation( |
service_id_, data.queried_name.c_str()); |
- const UniformInfo* info = AddUniformInfo( |
- data.size, data.type, location, data.corrected_name, |
- data.original_name); |
- if (info->IsSampler()) { |
- sampler_indices_.push_back(info->fake_location_base); |
+ // 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); |
} |
- max_uniform_name_length_ = |
- std::max(max_uniform_name_length_, |
- static_cast<GLsizei>(info->name.size())); |
} |
+ |
valid_ = true; |
} |
void ProgramManager::ProgramInfo::ExecuteBindAttribLocationCalls() { |
- for (std::map<std::string, GLint>::const_iterator it = |
- bind_attrib_location_map_.begin(); |
+ 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) |
@@ -318,6 +395,9 @@ GLint ProgramManager::ProgramInfo::GetUniformFakeLocation( |
const std::string& name) const { |
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)) { |
@@ -342,7 +422,8 @@ GLint ProgramManager::ProgramInfo::GetUniformFakeLocation( |
index = index * 10 + digit; |
} |
if (!bad && index >= 0 && index < info.size) { |
- return GLES2Util::MakeFakeLocation(info.fake_location_base, index); |
+ return ProgramManager::MakeFakeLocation( |
+ info.fake_location_base, index); |
} |
} |
} |
@@ -374,6 +455,9 @@ const ProgramManager::ProgramInfo::UniformInfo* |
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]; |
@@ -398,6 +482,19 @@ const std::string* ProgramManager::ProgramInfo::GetAttribMappedName( |
return NULL; |
} |
+bool ProgramManager::ProgramInfo::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 ProgramManager::ProgramInfo::GetCorrectedVariableInfo( |
@@ -435,15 +532,29 @@ void ProgramManager::ProgramInfo::GetCorrectedVariableInfo( |
*original_name = name; |
} |
-const ProgramManager::ProgramInfo::UniformInfo* |
- ProgramManager::ProgramInfo::AddUniformInfo( |
- GLsizei size, GLenum type, GLint location, |
- const std::string& name, const std::string& original_name) { |
+bool ProgramManager::ProgramInfo::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]"; |
- int uniform_index = uniform_infos_.size(); |
- uniform_infos_.push_back( |
- UniformInfo(size, type, uniform_index, original_name)); |
- UniformInfo& info = uniform_infos_.back(); |
+ 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); |
@@ -476,7 +587,30 @@ const ProgramManager::ProgramInfo::UniformInfo* |
(info.name.size() > 3 && |
info.name.rfind(kArraySpec) == info.name.size() - 3)); |
- return &info; |
+ 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 ProgramManager::ProgramInfo::UniformInfo* |
+ ProgramManager::ProgramInfo::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 ProgramManager::ProgramInfo::SetSamplers( |
@@ -489,6 +623,9 @@ bool ProgramManager::ProgramInfo::SetSamplers( |
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); |
@@ -517,7 +654,7 @@ void ProgramManager::ProgramInfo::GetProgramiv(GLenum pname, GLint* params) { |
*params = max_attrib_name_length_ + 1; |
break; |
case GL_ACTIVE_UNIFORMS: |
- *params = uniform_infos_.size(); |
+ *params = num_uniforms_; |
break; |
case GL_ACTIVE_UNIFORM_MAX_LENGTH: |
// Notice +1 to accomodate NULL terminator. |
@@ -593,8 +730,7 @@ bool ProgramManager::ProgramInfo::CanLink() const { |
bool ProgramManager::ProgramInfo::DetectAttribLocationBindingConflicts() const { |
std::set<GLint> location_binding_used; |
- for (std::map<std::string, GLint>::const_iterator it = |
- bind_attrib_location_map_.begin(); |
+ 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; |
@@ -638,11 +774,13 @@ void ProgramManager::ProgramInfo::GetProgramInfo( |
for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) { |
const UniformInfo& info = uniform_infos_[ii]; |
- num_locations += info.element_locations.size(); |
- total_string_size += info.name.size(); |
+ if (info.IsValid()) { |
+ num_locations += info.element_locations.size(); |
+ total_string_size += info.name.size(); |
+ } |
} |
- uint32 num_inputs = attrib_infos_.size() + uniform_infos_.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) + |
@@ -664,7 +802,7 @@ void ProgramManager::ProgramInfo::GetProgramInfo( |
header->link_status = link_status_; |
header->num_attribs = attrib_infos_.size(); |
- header->num_uniforms = uniform_infos_.size(); |
+ header->num_uniforms = num_uniforms_; |
for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) { |
const VertexAttribInfo& info = attrib_infos_[ii]; |
@@ -681,19 +819,20 @@ void ProgramManager::ProgramInfo::GetProgramInfo( |
for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) { |
const UniformInfo& info = uniform_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(); |
- DCHECK(static_cast<size_t>(info.size) == info.element_locations.size()); |
- for (size_t jj = 0; jj < info.element_locations.size(); ++jj) { |
- *locations++ = GLES2Util::SwizzleLocation( |
- GLES2Util::MakeFakeLocation(ii, jj)); |
+ 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; |
} |
- memcpy(strings, info.name.c_str(), info.name.size()); |
- strings += info.name.size(); |
- ++inputs; |
} |
DCHECK_EQ(ComputeOffset(header, strings), size); |
@@ -823,6 +962,10 @@ void ProgramManager::ClearUniforms(ProgramManager::ProgramInfo* info) { |
} |
} |
+int32 ProgramManager::MakeFakeLocation(int32 index, int32 element) { |
+ return index + element * 0x10000; |
+} |
+ |
} // namespace gles2 |
} // namespace gpu |