// 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 #include #include "base/logging.h" #include "base/strings/string_util.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 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() { } void Shader::Destroy() { if (service_id_) { DeleteServiceID(); } } void Shader::RequestCompile(scoped_refptr translator, TranslatedShaderSourceType type) { shader_state_ = kShaderStateCompileRequested; translator_ = translator; source_type_ = type; last_compiled_source_ = source_; } void Shader::DoCompile() { // We require that RequestCompile() must be called before DoCompile(), // so we can return early if the shader state is not what we expect. if (shader_state_ != kShaderStateCompileRequested) { 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_, &name_map_); if (!success) { return; } source_for_driver = translated_source_.c_str(); } glShaderSource(service_id_, 1, &source_for_driver, NULL); 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_; } } 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::IncUseCount() { ++use_count_; } void Shader::DecUseCount() { --use_count_; DCHECK_GE(use_count_, 0); if (service_id_ && use_count_ == 0 && marked_for_deletion_) { DeleteServiceID(); } } void Shader::MarkForDeletion() { DCHECK(!marked_for_deletion_); DCHECK_NE(service_id_, 0u); marked_for_deletion_ = true; if (use_count_ == 0) { DeleteServiceID(); } } void Shader::DeleteServiceID() { DCHECK_NE(service_id_, 0u); 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 : NULL; } 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 NULL; } 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 NULL; } 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 { NameMap::const_iterator it = name_map_.find(hashed_name); if (it != name_map_.end()) return &(it->second); return NULL; } const std::string* Shader::GetMappedName( const std::string& original_name) const { for (const auto& key_value : name_map_) { if (key_value.second == original_name) return &(key_value.first); } return NULL; } 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 : NULL; } 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 : NULL; } 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 : NULL; } 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() {} 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()); } } Shader* ShaderManager::CreateShader( GLuint client_id, GLuint service_id, GLenum shader_type) { std::pair result = shaders_.insert(std::make_pair( client_id, scoped_refptr( 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() : NULL; } 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::RemoveShader(Shader* shader) { DCHECK(shader); DCHECK(IsOwned(shader)); if (shader->IsDeleted() && !shader->InUse()) { 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(); RemoveShader(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(); RemoveShader(shader); } } // namespace gles2 } // namespace gpu