1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "gpu/command_buffer/service/shader_translator.h"
6
7#include <string.h>
8#include <GLES2/gl2.h>
9#include <algorithm>
10
11#include "base/at_exit.h"
12#include "base/debug/trace_event.h"
13#include "base/lazy_instance.h"
14#include "base/logging.h"
15#include "base/strings/string_number_conversions.h"
16
17namespace {
18
19using gpu::gles2::ShaderTranslator;
20
21class ShaderTranslatorInitializer {
22 public:
23  ShaderTranslatorInitializer() {
24    TRACE_EVENT0("gpu", "ShInitialize");
25    CHECK(ShInitialize());
26  }
27
28  ~ShaderTranslatorInitializer() {
29    TRACE_EVENT0("gpu", "ShFinalize");
30    ShFinalize();
31  }
32};
33
34base::LazyInstance<ShaderTranslatorInitializer> g_translator_initializer =
35    LAZY_INSTANCE_INITIALIZER;
36
37void GetVariableInfo(ShHandle compiler, ShShaderInfo var_type,
38                     ShaderTranslator::VariableMap* var_map) {
39  if (!var_map)
40    return;
41  var_map->clear();
42
43  size_t name_len = 0, mapped_name_len = 0;
44  switch (var_type) {
45    case SH_ACTIVE_ATTRIBUTES:
46      ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &name_len);
47      break;
48    case SH_ACTIVE_UNIFORMS:
49      ShGetInfo(compiler, SH_ACTIVE_UNIFORM_MAX_LENGTH, &name_len);
50      break;
51    case SH_VARYINGS:
52      ShGetInfo(compiler, SH_VARYING_MAX_LENGTH, &name_len);
53      break;
54    default: NOTREACHED();
55  }
56  ShGetInfo(compiler, SH_MAPPED_NAME_MAX_LENGTH, &mapped_name_len);
57  if (name_len <= 1 || mapped_name_len <= 1) return;
58  scoped_ptr<char[]> name(new char[name_len]);
59  scoped_ptr<char[]> mapped_name(new char[mapped_name_len]);
60
61  size_t num_vars = 0;
62  ShGetInfo(compiler, var_type, &num_vars);
63  for (size_t i = 0; i < num_vars; ++i) {
64    size_t len = 0;
65    int size = 0;
66    sh::GLenum type = GL_NONE;
67    ShPrecisionType precision = SH_PRECISION_UNDEFINED;
68    int static_use = 0;
69
70    ShGetVariableInfo(compiler, var_type, i,
71                      &len, &size, &type, &precision, &static_use,
72                      name.get(), mapped_name.get());
73
74    // In theory we should CHECK(len <= name_len - 1) here, but ANGLE needs
75    // to handle long struct field name mapping before we can do this.
76    // Also, we should modify the ANGLE interface to also return a length
77    // for mapped_name.
78    std::string name_string(name.get(), std::min(len, name_len - 1));
79    mapped_name.get()[mapped_name_len - 1] = '\0';
80
81    ShaderTranslator::VariableInfo info(
82        type, size, precision, static_use, name_string);
83    (*var_map)[mapped_name.get()] = info;
84  }
85}
86
87void GetNameHashingInfo(
88    ShHandle compiler, ShaderTranslator::NameMap* name_map) {
89  if (!name_map)
90    return;
91  name_map->clear();
92
93  size_t hashed_names_count = 0;
94  ShGetInfo(compiler, SH_HASHED_NAMES_COUNT, &hashed_names_count);
95  if (hashed_names_count == 0)
96    return;
97
98  size_t name_max_len = 0, hashed_name_max_len = 0;
99  ShGetInfo(compiler, SH_NAME_MAX_LENGTH, &name_max_len);
100  ShGetInfo(compiler, SH_HASHED_NAME_MAX_LENGTH, &hashed_name_max_len);
101
102  scoped_ptr<char[]> name(new char[name_max_len]);
103  scoped_ptr<char[]> hashed_name(new char[hashed_name_max_len]);
104
105  for (size_t i = 0; i < hashed_names_count; ++i) {
106    ShGetNameHashingEntry(compiler, i, name.get(), hashed_name.get());
107    (*name_map)[hashed_name.get()] = name.get();
108  }
109}
110
111}  // namespace
112
113namespace gpu {
114namespace gles2 {
115
116ShaderTranslator::DestructionObserver::DestructionObserver() {
117}
118
119ShaderTranslator::DestructionObserver::~DestructionObserver() {
120}
121
122ShaderTranslator::ShaderTranslator()
123    : compiler_(NULL),
124      implementation_is_glsl_es_(false),
125      driver_bug_workarounds_(static_cast<ShCompileOptions>(0)) {
126}
127
128bool ShaderTranslator::Init(
129    GLenum shader_type,
130    ShShaderSpec shader_spec,
131    const ShBuiltInResources* resources,
132    ShaderTranslatorInterface::GlslImplementationType glsl_implementation_type,
133    ShCompileOptions driver_bug_workarounds) {
134  // Make sure Init is called only once.
135  DCHECK(compiler_ == NULL);
136  DCHECK(shader_type == GL_FRAGMENT_SHADER || shader_type == GL_VERTEX_SHADER);
137  DCHECK(shader_spec == SH_GLES2_SPEC || shader_spec == SH_WEBGL_SPEC);
138  DCHECK(resources != NULL);
139
140  g_translator_initializer.Get();
141
142  ShShaderOutput shader_output =
143      (glsl_implementation_type == kGlslES ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT);
144
145  {
146    TRACE_EVENT0("gpu", "ShConstructCompiler");
147    compiler_ = ShConstructCompiler(
148        shader_type, shader_spec, shader_output, resources);
149  }
150  compiler_options_ = *resources;
151  implementation_is_glsl_es_ = (glsl_implementation_type == kGlslES);
152  driver_bug_workarounds_ = driver_bug_workarounds;
153  return compiler_ != NULL;
154}
155
156int ShaderTranslator::GetCompileOptions() const {
157  int compile_options =
158      SH_OBJECT_CODE | SH_VARIABLES | SH_ENFORCE_PACKING_RESTRICTIONS |
159      SH_LIMIT_EXPRESSION_COMPLEXITY | SH_LIMIT_CALL_STACK_DEPTH |
160      SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
161
162  compile_options |= driver_bug_workarounds_;
163
164  return compile_options;
165}
166
167bool ShaderTranslator::Translate(const std::string& shader_source,
168                                 std::string* info_log,
169                                 std::string* translated_source,
170                                 VariableMap* attrib_map,
171                                 VariableMap* uniform_map,
172                                 VariableMap* varying_map,
173                                 NameMap* name_map) const {
174  // Make sure this instance is initialized.
175  DCHECK(compiler_ != NULL);
176
177  bool success = false;
178  {
179    TRACE_EVENT0("gpu", "ShCompile");
180    const char* const shader_strings[] = { shader_source.c_str() };
181    success = !!ShCompile(
182        compiler_, shader_strings, 1, GetCompileOptions());
183  }
184  if (success) {
185    if (translated_source) {
186      translated_source->clear();
187      // Get translated shader.
188      size_t obj_code_len = 0;
189      ShGetInfo(compiler_, SH_OBJECT_CODE_LENGTH, &obj_code_len);
190      if (obj_code_len > 1) {
191        scoped_ptr<char[]> buffer(new char[obj_code_len]);
192        ShGetObjectCode(compiler_, buffer.get());
193        *translated_source = std::string(buffer.get(), obj_code_len - 1);
194      }
195    }
196    // Get info for attribs, uniforms, and varyings.
197    GetVariableInfo(compiler_, SH_ACTIVE_ATTRIBUTES, attrib_map);
198    GetVariableInfo(compiler_, SH_ACTIVE_UNIFORMS, uniform_map);
199    GetVariableInfo(compiler_, SH_VARYINGS, varying_map);
200    // Get info for name hashing.
201    GetNameHashingInfo(compiler_, name_map);
202  }
203
204  // Get info log.
205  if (info_log) {
206    info_log->clear();
207    size_t info_log_len = 0;
208    ShGetInfo(compiler_, SH_INFO_LOG_LENGTH, &info_log_len);
209    if (info_log_len > 1) {
210      scoped_ptr<char[]> buffer(new char[info_log_len]);
211      ShGetInfoLog(compiler_, buffer.get());
212      *info_log = std::string(buffer.get(), info_log_len - 1);
213    }
214  }
215
216  return success;
217}
218
219std::string ShaderTranslator::GetStringForOptionsThatWouldAffectCompilation()
220    const {
221  DCHECK(compiler_ != NULL);
222
223  size_t resource_len = 0;
224  ShGetInfo(compiler_, SH_RESOURCES_STRING_LENGTH, &resource_len);
225  DCHECK(resource_len > 1);
226  scoped_ptr<char[]> resource_str(new char[resource_len]);
227
228  ShGetBuiltInResourcesString(compiler_, resource_len, resource_str.get());
229
230  return std::string(":CompileOptions:" +
231         base::IntToString(GetCompileOptions())) +
232         std::string(resource_str.get());
233}
234
235void ShaderTranslator::AddDestructionObserver(
236    DestructionObserver* observer) {
237  destruction_observers_.AddObserver(observer);
238}
239
240void ShaderTranslator::RemoveDestructionObserver(
241    DestructionObserver* observer) {
242  destruction_observers_.RemoveObserver(observer);
243}
244
245ShaderTranslator::~ShaderTranslator() {
246  FOR_EACH_OBSERVER(DestructionObserver,
247                    destruction_observers_,
248                    OnDestruct(this));
249
250  if (compiler_ != NULL)
251    ShDestruct(compiler_);
252}
253
254}  // namespace gles2
255}  // namespace gpu
256
257