blob: 9e79fd90236fc1562045ee48422e1bddf95ae566 [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_translator.h"
#include <string.h>
#include <GLES2/gl2.h>
#include <algorithm>
#include "base/at_exit.h"
#include "base/debug/trace_event.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
namespace {
using gpu::gles2::ShaderTranslator;
class ShaderTranslatorInitializer {
public:
ShaderTranslatorInitializer() {
TRACE_EVENT0("gpu", "ShInitialize");
CHECK(ShInitialize());
}
~ShaderTranslatorInitializer() {
TRACE_EVENT0("gpu", "ShFinalize");
ShFinalize();
}
};
base::LazyInstance<ShaderTranslatorInitializer> g_translator_initializer =
LAZY_INSTANCE_INITIALIZER;
#if !defined(ANGLE_SH_VERSION) || ANGLE_SH_VERSION < 108
typedef int ANGLEGetInfoType;
#else
typedef size_t ANGLEGetInfoType;
#endif
void GetVariableInfo(ShHandle compiler, ShShaderInfo var_type,
ShaderTranslator::VariableMap* var_map) {
ANGLEGetInfoType name_len = 0, mapped_name_len = 0;
switch (var_type) {
case SH_ACTIVE_ATTRIBUTES:
ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &name_len);
break;
case SH_ACTIVE_UNIFORMS:
ShGetInfo(compiler, SH_ACTIVE_UNIFORM_MAX_LENGTH, &name_len);
break;
case SH_VARYINGS:
ShGetInfo(compiler, SH_VARYING_MAX_LENGTH, &name_len);
break;
default: NOTREACHED();
}
ShGetInfo(compiler, SH_MAPPED_NAME_MAX_LENGTH, &mapped_name_len);
if (name_len <= 1 || mapped_name_len <= 1) return;
scoped_ptr<char[]> name(new char[name_len]);
scoped_ptr<char[]> mapped_name(new char[mapped_name_len]);
ANGLEGetInfoType num_vars = 0;
ShGetInfo(compiler, var_type, &num_vars);
for (ANGLEGetInfoType i = 0; i < num_vars; ++i) {
ANGLEGetInfoType len = 0;
int size = 0;
#if (ANGLE_SH_VERSION >= 126)
sh::GLenum type = GL_NONE;
#else
ShDataType type = SH_NONE;
#endif
ShPrecisionType precision = SH_PRECISION_UNDEFINED;
int static_use = 0;
ShGetVariableInfo(compiler, var_type, i,
&len, &size, &type, &precision, &static_use,
name.get(), mapped_name.get());
// In theory we should CHECK(len <= name_len - 1) here, but ANGLE needs
// to handle long struct field name mapping before we can do this.
// Also, we should modify the ANGLE interface to also return a length
// for mapped_name.
std::string name_string(name.get(), std::min(len, name_len - 1));
mapped_name.get()[mapped_name_len - 1] = '\0';
ShaderTranslator::VariableInfo info(
type, size, precision, static_use, name_string);
(*var_map)[mapped_name.get()] = info;
}
}
void GetNameHashingInfo(
ShHandle compiler, ShaderTranslator::NameMap* name_map) {
ANGLEGetInfoType hashed_names_count = 0;
ShGetInfo(compiler, SH_HASHED_NAMES_COUNT, &hashed_names_count);
if (hashed_names_count == 0)
return;
ANGLEGetInfoType name_max_len = 0, hashed_name_max_len = 0;
ShGetInfo(compiler, SH_NAME_MAX_LENGTH, &name_max_len);
ShGetInfo(compiler, SH_HASHED_NAME_MAX_LENGTH, &hashed_name_max_len);
scoped_ptr<char[]> name(new char[name_max_len]);
scoped_ptr<char[]> hashed_name(new char[hashed_name_max_len]);
for (ANGLEGetInfoType i = 0; i < hashed_names_count; ++i) {
ShGetNameHashingEntry(compiler, i, name.get(), hashed_name.get());
(*name_map)[hashed_name.get()] = name.get();
}
}
} // namespace
namespace gpu {
namespace gles2 {
ShaderTranslator::DestructionObserver::DestructionObserver() {
}
ShaderTranslator::DestructionObserver::~DestructionObserver() {
}
ShaderTranslator::ShaderTranslator()
: compiler_(NULL),
implementation_is_glsl_es_(false),
driver_bug_workarounds_(static_cast<ShCompileOptions>(0)) {
}
bool ShaderTranslator::Init(
#if (ANGLE_SH_VERSION >= 126)
GLenum shader_type,
#else
ShShaderType shader_type,
#endif
ShShaderSpec shader_spec,
const ShBuiltInResources* resources,
ShaderTranslatorInterface::GlslImplementationType glsl_implementation_type,
ShCompileOptions driver_bug_workarounds) {
// Make sure Init is called only once.
DCHECK(compiler_ == NULL);
#if (ANGLE_SH_VERSION >= 126)
DCHECK(shader_type == GL_FRAGMENT_SHADER || shader_type == GL_VERTEX_SHADER);
#else
DCHECK(shader_type == SH_FRAGMENT_SHADER || shader_type == SH_VERTEX_SHADER);
#endif
DCHECK(shader_spec == SH_GLES2_SPEC || shader_spec == SH_WEBGL_SPEC);
DCHECK(resources != NULL);
g_translator_initializer.Get();
ShShaderOutput shader_output =
(glsl_implementation_type == kGlslES ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT);
{
TRACE_EVENT0("gpu", "ShConstructCompiler");
compiler_ = ShConstructCompiler(
shader_type, shader_spec, shader_output, resources);
}
compiler_options_ = *resources;
implementation_is_glsl_es_ = (glsl_implementation_type == kGlslES);
driver_bug_workarounds_ = driver_bug_workarounds;
return compiler_ != NULL;
}
int ShaderTranslator::GetCompileOptions() const {
int compile_options =
SH_OBJECT_CODE | SH_VARIABLES | SH_ENFORCE_PACKING_RESTRICTIONS |
SH_LIMIT_EXPRESSION_COMPLEXITY | SH_LIMIT_CALL_STACK_DEPTH |
SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
compile_options |= driver_bug_workarounds_;
return compile_options;
}
bool ShaderTranslator::Translate(const char* shader) {
// Make sure this instance is initialized.
DCHECK(compiler_ != NULL);
DCHECK(shader != NULL);
ClearResults();
bool success = false;
{
TRACE_EVENT0("gpu", "ShCompile");
success = !!ShCompile(compiler_, &shader, 1, GetCompileOptions());
}
if (success) {
// Get translated shader.
ANGLEGetInfoType obj_code_len = 0;
ShGetInfo(compiler_, SH_OBJECT_CODE_LENGTH, &obj_code_len);
if (obj_code_len > 1) {
translated_shader_.reset(new char[obj_code_len]);
ShGetObjectCode(compiler_, translated_shader_.get());
}
// Get info for attribs and uniforms.
GetVariableInfo(compiler_, SH_ACTIVE_ATTRIBUTES, &attrib_map_);
GetVariableInfo(compiler_, SH_ACTIVE_UNIFORMS, &uniform_map_);
GetVariableInfo(compiler_, SH_VARYINGS, &varying_map_);
// Get info for name hashing.
GetNameHashingInfo(compiler_, &name_map_);
}
// Get info log.
ANGLEGetInfoType info_log_len = 0;
ShGetInfo(compiler_, SH_INFO_LOG_LENGTH, &info_log_len);
if (info_log_len > 1) {
info_log_.reset(new char[info_log_len]);
ShGetInfoLog(compiler_, info_log_.get());
} else {
info_log_.reset();
}
return success;
}
std::string ShaderTranslator::GetStringForOptionsThatWouldAffectCompilation()
const {
#if ANGLE_SH_VERSION >= 124
DCHECK(compiler_ != NULL);
ANGLEGetInfoType resource_len = 0;
ShGetInfo(compiler_, SH_RESOURCES_STRING_LENGTH, &resource_len);
DCHECK(resource_len > 1);
scoped_ptr<char[]> resource_str(new char[resource_len]);
ShGetBuiltInResourcesString(compiler_, resource_len, resource_str.get());
return std::string(":CompileOptions:" +
base::IntToString(GetCompileOptions())) +
std::string(resource_str.get());
#else
#if ANGLE_SH_VERSION >= 123
const size_t kNumIntFields = 21;
#elif ANGLE_SH_VERSION >= 122
const size_t kNumIntFields = 20;
#else
const size_t kNumIntFields = 16;
#endif
const size_t kNumEnumFields = 1;
const size_t kNumFunctionPointerFields = 1;
struct MustMatchShBuiltInResource {
typedef khronos_uint64_t (*FunctionPointer)(const char*, size_t);
enum Enum {
kFirst,
};
int int_fields[kNumIntFields];
FunctionPointer pointer_fields[kNumFunctionPointerFields];
Enum enum_fields[kNumEnumFields];
};
// If this assert fails most likely that means something below needs updating.
COMPILE_ASSERT(
sizeof(ShBuiltInResources) == sizeof(MustMatchShBuiltInResource),
Fields_Have_Changed_In_ShBuiltInResource_So_Update_Below);
return std::string(
":CompileOptions:" +
base::IntToString(GetCompileOptions()) +
":MaxVertexAttribs:" +
base::IntToString(compiler_options_.MaxVertexAttribs) +
":MaxVertexUniformVectors:" +
base::IntToString(compiler_options_.MaxVertexUniformVectors) +
":MaxVaryingVectors:" +
base::IntToString(compiler_options_.MaxVaryingVectors) +
":MaxVertexTextureImageUnits:" +
base::IntToString(compiler_options_.MaxVertexTextureImageUnits) +
":MaxCombinedTextureImageUnits:" +
base::IntToString(compiler_options_.MaxCombinedTextureImageUnits) +
":MaxTextureImageUnits:" +
base::IntToString(compiler_options_.MaxTextureImageUnits) +
":MaxFragmentUniformVectors:" +
base::IntToString(compiler_options_.MaxFragmentUniformVectors) +
":MaxDrawBuffers:" +
base::IntToString(compiler_options_.MaxDrawBuffers) +
":OES_standard_derivatives:" +
base::IntToString(compiler_options_.OES_standard_derivatives) +
":OES_EGL_image_external:" +
base::IntToString(compiler_options_.OES_EGL_image_external) +
":ARB_texture_rectangle:" +
base::IntToString(compiler_options_.ARB_texture_rectangle) +
":EXT_draw_buffers:" +
base::IntToString(compiler_options_.EXT_draw_buffers) +
":FragmentPrecisionHigh:" +
base::IntToString(compiler_options_.FragmentPrecisionHigh) +
":MaxExpressionComplexity:" +
base::IntToString(compiler_options_.MaxExpressionComplexity) +
":MaxCallStackDepth:" +
base::IntToString(compiler_options_.MaxCallStackDepth) +
":EXT_frag_depth:" +
#if ANGLE_SH_VERSION >= 122
base::IntToString(compiler_options_.EXT_frag_depth) +
#if ANGLE_SH_VERSION >= 123
":EXT_shader_texture_lod:" +
base::IntToString(compiler_options_.EXT_shader_texture_lod) +
#endif
":MaxVertexOutputVectors:" +
base::IntToString(compiler_options_.MaxVertexOutputVectors) +
":MaxFragmentInputVectors:" +
base::IntToString(compiler_options_.MaxFragmentInputVectors) +
":MinProgramTexelOffset:" +
base::IntToString(compiler_options_.MinProgramTexelOffset) +
":MaxProgramTexelOffset:" +
base::IntToString(compiler_options_.MaxProgramTexelOffset));
#else // ANGLE_SH_VERSION < 122
base::IntToString(compiler_options_.EXT_frag_depth));
#endif
#endif
}
const char* ShaderTranslator::translated_shader() const {
return translated_shader_.get();
}
const char* ShaderTranslator::info_log() const {
return info_log_.get();
}
const ShaderTranslatorInterface::VariableMap&
ShaderTranslator::attrib_map() const {
return attrib_map_;
}
const ShaderTranslatorInterface::VariableMap&
ShaderTranslator::uniform_map() const {
return uniform_map_;
}
const ShaderTranslatorInterface::VariableMap&
ShaderTranslator::varying_map() const {
return varying_map_;
}
const ShaderTranslatorInterface::NameMap&
ShaderTranslator::name_map() const {
return name_map_;
}
void ShaderTranslator::AddDestructionObserver(
DestructionObserver* observer) {
destruction_observers_.AddObserver(observer);
}
void ShaderTranslator::RemoveDestructionObserver(
DestructionObserver* observer) {
destruction_observers_.RemoveObserver(observer);
}
ShaderTranslator::~ShaderTranslator() {
FOR_EACH_OBSERVER(DestructionObserver,
destruction_observers_,
OnDestruct(this));
if (compiler_ != NULL)
ShDestruct(compiler_);
}
void ShaderTranslator::ClearResults() {
translated_shader_.reset();
info_log_.reset();
attrib_map_.clear();
uniform_map_.clear();
varying_map_.clear();
name_map_.clear();
}
} // namespace gles2
} // namespace gpu