blob: 44f5b2df2d8d33670b15223ff6a1c8e100c3486d [file] [log] [blame]
// 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