| // 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/shader_manager.h" |
| |
| #include <stddef.h> |
| |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "ui/gl/progress_reporter.h" |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| namespace { |
| |
| // Given a variable name | a[0].b.c[0] |, return |a|. |
| std::string GetTopVariableName(const std::string& fullname) { |
| size_t pos = fullname.find_first_of("[."); |
| if (pos == std::string::npos) |
| return fullname; |
| return fullname.substr(0, pos); |
| } |
| |
| } // namespace anonymous |
| |
| void CompileShaderWithLog(GLuint shader, const char* shader_source) { |
| glShaderSource(shader, 1, &shader_source, 0); |
| glCompileShader(shader); |
| #if DCHECK_IS_ON() |
| GLint compile_status = GL_FALSE; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); |
| if (GL_TRUE != compile_status) { |
| GLint info_log_length; |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length); |
| std::vector<GLchar> info_log(info_log_length); |
| glGetShaderInfoLog(shader, info_log_length, nullptr, &info_log[0]); |
| std::string log(&info_log[0], info_log_length - 1); |
| DLOG(ERROR) << "Error compiling shader: " << log; |
| DLOG(ERROR) << "Shader compilation failure."; |
| } |
| #endif |
| } |
| |
| Shader::Shader(GLuint service_id, GLenum shader_type) |
| : use_count_(0), |
| shader_state_(kShaderStateWaiting), |
| marked_for_deletion_(false), |
| service_id_(service_id), |
| shader_type_(shader_type), |
| shader_version_(kUndefinedShaderVersion), |
| source_type_(kANGLE), |
| valid_(false) { |
| } |
| |
| Shader::~Shader() = default; |
| |
| void Shader::Destroy() { |
| DeleteServiceID(); |
| } |
| |
| void Shader::RequestCompile(scoped_refptr<ShaderTranslatorInterface> translator, |
| TranslatedShaderSourceType type) { |
| shader_state_ = kShaderStateCompileRequested; |
| translator_ = translator; |
| if (translator_) { |
| options_affecting_compilation_ = |
| translator_->GetStringForOptionsThatWouldAffectCompilation(); |
| } |
| source_type_ = type; |
| last_compiled_source_ = source_; |
| } |
| |
| void Shader::DoCompile() { |
| if (!CanCompile()) { |
| return; |
| } |
| |
| // Signify the shader has been compiled, whether or not it is valid |
| // is dependent on the |valid_| member variable. |
| shader_state_ = kShaderStateCompiled; |
| valid_ = false; |
| |
| // Translate GL ES 2.0 shader to Desktop GL shader and pass that to |
| // glShaderSource and then glCompileShader. |
| const char* source_for_driver = last_compiled_source_.c_str(); |
| ShaderTranslatorInterface* translator = translator_.get(); |
| if (translator) { |
| bool success = translator->Translate( |
| last_compiled_source_, &log_info_, &translated_source_, |
| &shader_version_, &attrib_map_, &uniform_map_, &varying_map_, |
| &interface_block_map_, &output_variable_list_); |
| if (!success) { |
| return; |
| } |
| source_for_driver = translated_source_.c_str(); |
| } |
| |
| glShaderSource(service_id_, 1, &source_for_driver, nullptr); |
| glCompileShader(service_id_); |
| |
| if (source_type_ == kANGLE) { |
| RefreshTranslatedShaderSource(); |
| source_for_driver = translated_source_.c_str(); |
| } |
| |
| GLint status = GL_FALSE; |
| glGetShaderiv(service_id_, GL_COMPILE_STATUS, &status); |
| if (status == GL_TRUE) { |
| valid_ = true; |
| } else { |
| valid_ = false; |
| |
| // 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. |
| std::string translator_log = log_info_; |
| |
| GLint max_len = 0; |
| glGetShaderiv(service_id_, GL_INFO_LOG_LENGTH, &max_len); |
| log_info_.resize(max_len); |
| if (max_len) { |
| GLint len = 0; |
| glGetShaderInfoLog(service_id_, log_info_.size(), &len, &log_info_.at(0)); |
| DCHECK(max_len == 0 || len < max_len); |
| DCHECK(len == 0 || log_info_[len] == '\0'); |
| log_info_.resize(len); |
| } |
| |
| LOG_IF(ERROR, translator) |
| << "Shader translator allowed/produced an invalid shader " |
| << "unless the driver is buggy:" |
| << "\n--Log from shader translator--\n" << translator_log |
| << "\n--original-shader--\n" << last_compiled_source_ |
| << "\n--translated-shader--\n" << source_for_driver |
| << "\n--info-log--\n" << log_info_; |
| } |
| |
| // Translator is no longer required and can be released |
| translator_ = nullptr; |
| } |
| |
| void Shader::RefreshTranslatedShaderSource() { |
| if (source_type_ == kANGLE) { |
| GLint max_len = 0; |
| glGetShaderiv(service_id_, GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE, |
| &max_len); |
| translated_source_.resize(max_len); |
| if (max_len) { |
| GLint len = 0; |
| glGetTranslatedShaderSourceANGLE(service_id_, translated_source_.size(), |
| &len, &translated_source_.at(0)); |
| DCHECK(max_len == 0 || len < max_len); |
| DCHECK(len == 0 || translated_source_[len] == '\0'); |
| translated_source_.resize(len); |
| } |
| } |
| } |
| |
| void Shader::MarkForDeletion() { |
| DCHECK(!marked_for_deletion_); |
| DCHECK_NE(service_id_, 0u); |
| |
| marked_for_deletion_ = true; |
| } |
| |
| void Shader::DeleteServiceID() { |
| if (service_id_) { |
| glDeleteShader(service_id_); |
| service_id_ = 0; |
| } |
| } |
| |
| const sh::Attribute* Shader::GetAttribInfo(const std::string& name) const { |
| // Vertex attributes can't be arrays or structs (GLSL ES 3.00.4, section |
| // 4.3.4, "Input Variables"), so |name| is the top level name used as |
| // the AttributeMap key. |
| AttributeMap::const_iterator it = attrib_map_.find(name); |
| return it != attrib_map_.end() ? &it->second : nullptr; |
| } |
| |
| const std::string* Shader::GetAttribMappedName( |
| const std::string& original_name) const { |
| for (const auto& key_value : attrib_map_) { |
| if (key_value.second.name == original_name) |
| return &(key_value.first); |
| } |
| return nullptr; |
| } |
| |
| const std::string* Shader::GetUniformMappedName( |
| const std::string& original_name) const { |
| for (const auto& key_value : uniform_map_) { |
| if (key_value.second.name == original_name) |
| return &(key_value.first); |
| } |
| return nullptr; |
| } |
| |
| const std::string* Shader::GetVaryingMappedName( |
| const std::string& original_name) const { |
| for (VaryingMap::const_iterator it = varying_map_.begin(); |
| it != varying_map_.end(); ++it) { |
| if (it->second.name == original_name) |
| return &(it->first); |
| } |
| return nullptr; |
| } |
| |
| const std::string* Shader::GetInterfaceBlockMappedName( |
| const std::string& original_name) const { |
| for (const auto& key_value : interface_block_map_) { |
| if (key_value.second.name == original_name) |
| return &(key_value.first); |
| } |
| return nullptr; |
| } |
| |
| const std::string* Shader::GetOutputVariableMappedName( |
| const std::string& original_name) const { |
| for (const auto& value : output_variable_list_) { |
| if (value.name == original_name) |
| return &value.mappedName; |
| } |
| return nullptr; |
| } |
| |
| const std::string* Shader::GetOriginalNameFromHashedName( |
| const std::string& hashed_name) const { |
| if (const auto* info = GetAttribInfo(hashed_name)) { |
| return &info->name; |
| } |
| if (const auto* info = GetUniformInfo(hashed_name)) { |
| return &info->name; |
| } |
| if (const auto* info = GetVaryingInfo(hashed_name)) { |
| return &info->name; |
| } |
| if (const auto* info = GetInterfaceBlockInfo(hashed_name)) { |
| return &info->name; |
| } |
| if (const auto* info = GetOutputVariableInfo(hashed_name)) { |
| return &info->name; |
| } |
| return nullptr; |
| } |
| |
| const sh::Uniform* Shader::GetUniformInfo(const std::string& name) const { |
| UniformMap::const_iterator it = uniform_map_.find(GetTopVariableName(name)); |
| return it != uniform_map_.end() ? &it->second : nullptr; |
| } |
| |
| const sh::Varying* Shader::GetVaryingInfo(const std::string& name) const { |
| VaryingMap::const_iterator it = varying_map_.find(GetTopVariableName(name)); |
| return it != varying_map_.end() ? &it->second : nullptr; |
| } |
| |
| const sh::InterfaceBlock* Shader::GetInterfaceBlockInfo( |
| const std::string& name) const { |
| InterfaceBlockMap::const_iterator it = |
| interface_block_map_.find(GetTopVariableName(name)); |
| return it != interface_block_map_.end() ? &it->second : nullptr; |
| } |
| |
| const sh::OutputVariable* Shader::GetOutputVariableInfo( |
| const std::string& name) const { |
| std::string mapped_name = GetTopVariableName(name); |
| // Number of output variables is expected to be so low that |
| // a linear search of a list should be faster than using a map. |
| for (const auto& value : output_variable_list_) { |
| if (value.mappedName == mapped_name) |
| return &value; |
| } |
| return nullptr; |
| } |
| |
| ShaderManager::ShaderManager(gl::ProgressReporter* progress_reporter) |
| : progress_reporter_(progress_reporter) {} |
| |
| ShaderManager::~ShaderManager() { |
| DCHECK(shaders_.empty()); |
| } |
| |
| void ShaderManager::Destroy(bool have_context) { |
| while (!shaders_.empty()) { |
| if (have_context) { |
| Shader* shader = shaders_.begin()->second.get(); |
| shader->Destroy(); |
| } |
| shaders_.erase(shaders_.begin()); |
| if (progress_reporter_) |
| progress_reporter_->ReportProgress(); |
| } |
| } |
| |
| Shader* ShaderManager::CreateShader( |
| GLuint client_id, |
| GLuint service_id, |
| GLenum shader_type) { |
| std::pair<ShaderMap::iterator, bool> result = |
| shaders_.insert(std::make_pair( |
| client_id, scoped_refptr<Shader>( |
| new Shader(service_id, shader_type)))); |
| DCHECK(result.second); |
| return result.first->second.get(); |
| } |
| |
| Shader* ShaderManager::GetShader(GLuint client_id) { |
| ShaderMap::iterator it = shaders_.find(client_id); |
| return it != shaders_.end() ? it->second.get() : nullptr; |
| } |
| |
| bool ShaderManager::GetClientId(GLuint service_id, GLuint* client_id) const { |
| // This doesn't need to be fast. It's only used during slow queries. |
| for (ShaderMap::const_iterator it = shaders_.begin(); |
| it != shaders_.end(); ++it) { |
| if (it->second->service_id() == service_id) { |
| *client_id = it->first; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool ShaderManager::IsOwned(Shader* shader) { |
| for (ShaderMap::iterator it = shaders_.begin(); |
| it != shaders_.end(); ++it) { |
| if (it->second.get() == shader) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void ShaderManager::RemoveShaderIfUnused(Shader* shader) { |
| DCHECK(shader); |
| DCHECK(IsOwned(shader)); |
| if (shader->IsDeleted() && !shader->InUse()) { |
| shader->DeleteServiceID(); |
| for (ShaderMap::iterator it = shaders_.begin(); |
| it != shaders_.end(); ++it) { |
| if (it->second.get() == shader) { |
| shaders_.erase(it); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| } |
| |
| void ShaderManager::Delete(Shader* shader) { |
| DCHECK(shader); |
| DCHECK(IsOwned(shader)); |
| shader->MarkForDeletion(); |
| RemoveShaderIfUnused(shader); |
| } |
| |
| void ShaderManager::UseShader(Shader* shader) { |
| DCHECK(shader); |
| DCHECK(IsOwned(shader)); |
| shader->IncUseCount(); |
| } |
| |
| void ShaderManager::UnuseShader(Shader* shader) { |
| DCHECK(shader); |
| DCHECK(IsOwned(shader)); |
| shader->DecUseCount(); |
| RemoveShaderIfUnused(shader); |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |