| // 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 |
| |