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/client/program_info_manager.h"
6
7#include <map>
8
9#include "base/compiler_specific.h"
10#include "base/synchronization/lock.h"
11#include "gpu/command_buffer/client/gles2_implementation.h"
12#include "gpu/command_buffer/common/gles2_cmd_utils.h"
13
14namespace gpu {
15namespace gles2 {
16
17class NonCachedProgramInfoManager : public ProgramInfoManager {
18 public:
19  NonCachedProgramInfoManager();
20  virtual ~NonCachedProgramInfoManager();
21
22  virtual void CreateInfo(GLuint program) OVERRIDE;
23
24  virtual void DeleteInfo(GLuint program) OVERRIDE;
25
26  virtual bool GetProgramiv(GLES2Implementation* gl,
27                            GLuint program,
28                            GLenum pname,
29                            GLint* params) OVERRIDE;
30
31  virtual GLint GetAttribLocation(GLES2Implementation* gl,
32                                  GLuint program,
33                                  const char* name) OVERRIDE;
34
35  virtual GLint GetUniformLocation(GLES2Implementation* gl,
36                                   GLuint program,
37                                   const char* name) OVERRIDE;
38
39  virtual bool GetActiveAttrib(GLES2Implementation* gl,
40                               GLuint program,
41                               GLuint index,
42                               GLsizei bufsize,
43                               GLsizei* length,
44                               GLint* size,
45                               GLenum* type,
46                               char* name) OVERRIDE;
47
48  virtual bool GetActiveUniform(GLES2Implementation* gl,
49                                GLuint program,
50                                GLuint index,
51                                GLsizei bufsize,
52                                GLsizei* length,
53                                GLint* size,
54                                GLenum* type,
55                                char* name) OVERRIDE;
56
57};
58
59NonCachedProgramInfoManager::NonCachedProgramInfoManager() {
60}
61
62NonCachedProgramInfoManager::~NonCachedProgramInfoManager() {
63}
64
65void NonCachedProgramInfoManager::CreateInfo(GLuint /* program */) {
66}
67
68void NonCachedProgramInfoManager::DeleteInfo(GLuint /* program */) {
69}
70
71bool NonCachedProgramInfoManager::GetProgramiv(
72    GLES2Implementation* /* gl */,
73    GLuint /* program */,
74    GLenum /* pname */,
75    GLint* /* params */) {
76  return false;
77}
78
79GLint NonCachedProgramInfoManager::GetAttribLocation(
80    GLES2Implementation* gl, GLuint program, const char* name) {
81  return gl->GetAttribLocationHelper(program, name);
82}
83
84GLint NonCachedProgramInfoManager::GetUniformLocation(
85    GLES2Implementation* gl, GLuint program, const char* name) {
86  return gl->GetUniformLocationHelper(program, name);
87}
88
89bool NonCachedProgramInfoManager::GetActiveAttrib(
90    GLES2Implementation* gl,
91    GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
92    GLint* size, GLenum* type, char* name) {
93  return gl->GetActiveAttribHelper(
94      program, index, bufsize, length, size, type, name);
95}
96
97bool NonCachedProgramInfoManager::GetActiveUniform(
98    GLES2Implementation* gl,
99    GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
100    GLint* size, GLenum* type, char* name) {
101  return gl->GetActiveUniformHelper(
102      program, index, bufsize, length, size, type, name);
103}
104
105class CachedProgramInfoManager : public ProgramInfoManager {
106 public:
107  CachedProgramInfoManager();
108  virtual ~CachedProgramInfoManager();
109
110  virtual void CreateInfo(GLuint program) OVERRIDE;
111
112  virtual void DeleteInfo(GLuint program) OVERRIDE;
113
114  virtual bool GetProgramiv(GLES2Implementation* gl,
115                            GLuint program,
116                            GLenum pname,
117                            GLint* params) OVERRIDE;
118
119  virtual GLint GetAttribLocation(GLES2Implementation* gl,
120                                  GLuint program,
121                                  const char* name) OVERRIDE;
122
123  virtual GLint GetUniformLocation(GLES2Implementation* gl,
124                                   GLuint program,
125                                   const char* name) OVERRIDE;
126
127  virtual bool GetActiveAttrib(GLES2Implementation* gl,
128                               GLuint program,
129                               GLuint index,
130                               GLsizei bufsize,
131                               GLsizei* length,
132                               GLint* size,
133                               GLenum* type,
134                               char* name) OVERRIDE;
135
136  virtual bool GetActiveUniform(GLES2Implementation* gl,
137                                GLuint program,
138                                GLuint index,
139                                GLsizei bufsize,
140                                GLsizei* length,
141                                GLint* size,
142                                GLenum* type,
143                                char* name) OVERRIDE;
144
145 private:
146  class Program {
147   public:
148    struct UniformInfo {
149      UniformInfo(GLsizei _size, GLenum _type, const std::string& _name);
150
151      GLsizei size;
152      GLenum type;
153      bool is_array;
154      std::string name;
155      std::vector<GLint> element_locations;
156    };
157    struct VertexAttrib {
158      VertexAttrib(GLsizei _size, GLenum _type, const std::string& _name,
159                       GLint _location)
160          : size(_size),
161            type(_type),
162            location(_location),
163            name(_name) {
164      }
165      GLsizei size;
166      GLenum type;
167      GLint location;
168      std::string name;
169    };
170
171    typedef std::vector<UniformInfo> UniformInfoVector;
172    typedef std::vector<VertexAttrib> AttribInfoVector;
173
174    Program();
175
176    const AttribInfoVector& GetAttribInfos() const {
177      return attrib_infos_;
178    }
179
180    const VertexAttrib* GetAttribInfo(GLint index) const {
181      return (static_cast<size_t>(index) < attrib_infos_.size()) ?
182         &attrib_infos_[index] : NULL;
183    }
184
185    GLint GetAttribLocation(const std::string& name) const;
186
187    const UniformInfo* GetUniformInfo(GLint index) const {
188      return (static_cast<size_t>(index) < uniform_infos_.size()) ?
189         &uniform_infos_[index] : NULL;
190    }
191
192    // Gets the location of a uniform by name.
193    GLint GetUniformLocation(const std::string& name) const;
194
195    bool GetProgramiv(GLenum pname, GLint* params);
196
197    // Updates the program info after a successful link.
198    void Update(GLES2Implementation* gl, GLuint program);
199
200   private:
201    bool cached_;
202
203    GLsizei max_attrib_name_length_;
204
205    // Attrib by index.
206    AttribInfoVector attrib_infos_;
207
208    GLsizei max_uniform_name_length_;
209
210    // Uniform info by index.
211    UniformInfoVector uniform_infos_;
212
213    // This is true if glLinkProgram was successful last time it was called.
214    bool link_status_;
215  };
216
217  Program* GetProgramInfo(GLES2Implementation* gl, GLuint program);
218
219  // TODO(gman): Switch to a faster container.
220  typedef std::map<GLuint, Program> ProgramInfoMap;
221
222  ProgramInfoMap program_infos_;
223
224  mutable base::Lock lock_;
225};
226
227CachedProgramInfoManager::Program::UniformInfo::UniformInfo(
228    GLsizei _size, GLenum _type, const std::string& _name)
229    : size(_size),
230      type(_type),
231      name(_name) {
232  is_array = (!name.empty() && name[name.size() - 1] == ']');
233  DCHECK(!(size > 1 && !is_array));
234}
235
236CachedProgramInfoManager::Program::Program()
237    : cached_(false),
238      max_attrib_name_length_(0),
239      max_uniform_name_length_(0),
240      link_status_(false) {
241}
242
243// TODO(gman): Add a faster lookup.
244GLint CachedProgramInfoManager::Program::GetAttribLocation(
245    const std::string& name) const {
246  for (GLuint ii = 0; ii < attrib_infos_.size(); ++ii) {
247    const VertexAttrib& info = attrib_infos_[ii];
248    if (info.name == name) {
249      return info.location;
250    }
251  }
252  return -1;
253}
254
255GLint CachedProgramInfoManager::Program::GetUniformLocation(
256    const std::string& name) const {
257  bool getting_array_location = false;
258  size_t open_pos = std::string::npos;
259  int index = 0;
260  if (!GLES2Util::ParseUniformName(
261      name, &open_pos, &index, &getting_array_location)) {
262    return -1;
263  }
264  for (GLuint ii = 0; ii < uniform_infos_.size(); ++ii) {
265    const UniformInfo& info = uniform_infos_[ii];
266    if (info.name == name ||
267        (info.is_array &&
268         info.name.compare(0, info.name.size() - 3, name) == 0)) {
269      return info.element_locations[0];
270    } else if (getting_array_location && info.is_array) {
271      // Look for an array specification.
272      size_t open_pos_2 = info.name.find_last_of('[');
273      if (open_pos_2 == open_pos &&
274          name.compare(0, open_pos, info.name, 0, open_pos) == 0) {
275        if (index >= 0 && index < info.size) {
276          return info.element_locations[index];
277        }
278      }
279    }
280  }
281  return -1;
282}
283
284bool CachedProgramInfoManager::Program::GetProgramiv(
285    GLenum pname, GLint* params) {
286  switch (pname) {
287    case GL_LINK_STATUS:
288      *params = link_status_;
289      return true;
290    case GL_ACTIVE_ATTRIBUTES:
291      *params = attrib_infos_.size();
292      return true;
293    case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
294      *params = max_attrib_name_length_;
295      return true;
296    case GL_ACTIVE_UNIFORMS:
297      *params = uniform_infos_.size();
298      return true;
299    case GL_ACTIVE_UNIFORM_MAX_LENGTH:
300      *params = max_uniform_name_length_;
301      return true;
302    default:
303      break;
304  }
305  return false;
306}
307
308template<typename T> static T LocalGetAs(
309    const std::vector<int8>& data, uint32 offset, size_t size) {
310  const int8* p = &data[0] + offset;
311  if (offset + size > data.size()) {
312    NOTREACHED();
313    return NULL;
314  }
315  return static_cast<T>(static_cast<const void*>(p));
316}
317
318void CachedProgramInfoManager::Program::Update(
319    GLES2Implementation* gl, GLuint program) {
320  if (cached_) {
321    return;
322  }
323  std::vector<int8> result;
324  gl->GetProgramInfoCHROMIUMHelper(program, &result);
325  if (result.empty()) {
326    // This should only happen on a lost context.
327    return;
328  }
329  DCHECK_GE(result.size(), sizeof(ProgramInfoHeader));
330  const ProgramInfoHeader* header = LocalGetAs<const ProgramInfoHeader*>(
331      result, 0, sizeof(header));
332  link_status_ = header->link_status != 0;
333  if (!link_status_) {
334    return;
335  }
336  attrib_infos_.clear();
337  uniform_infos_.clear();
338  max_attrib_name_length_ = 0;
339  max_uniform_name_length_ = 0;
340  const ProgramInput* inputs = LocalGetAs<const ProgramInput*>(
341      result, sizeof(*header),
342      sizeof(ProgramInput) * (header->num_attribs + header->num_uniforms));
343  const ProgramInput* input = inputs;
344  for (uint32 ii = 0; ii < header->num_attribs; ++ii) {
345    const int32* location = LocalGetAs<const int32*>(
346        result, input->location_offset, sizeof(int32));
347    const char* name_buf = LocalGetAs<const char*>(
348        result, input->name_offset, input->name_length);
349    std::string name(name_buf, input->name_length);
350    attrib_infos_.push_back(
351        VertexAttrib(input->size, input->type, name, *location));
352    max_attrib_name_length_ = std::max(
353        static_cast<GLsizei>(name.size() + 1), max_attrib_name_length_);
354    ++input;
355  }
356  for (uint32 ii = 0; ii < header->num_uniforms; ++ii) {
357    const int32* locations = LocalGetAs<const int32*>(
358        result, input->location_offset, sizeof(int32) * input->size);
359    const char* name_buf = LocalGetAs<const char*>(
360        result, input->name_offset, input->name_length);
361    std::string name(name_buf, input->name_length);
362    UniformInfo info(input->size, input->type, name);
363    max_uniform_name_length_ = std::max(
364        static_cast<GLsizei>(name.size() + 1), max_uniform_name_length_);
365    for (int32 jj = 0; jj < input->size; ++jj) {
366      info.element_locations.push_back(locations[jj]);
367    }
368    uniform_infos_.push_back(info);
369    ++input;
370  }
371  DCHECK_EQ(header->num_attribs + header->num_uniforms,
372                static_cast<uint32>(input - inputs));
373  cached_ = true;
374}
375
376CachedProgramInfoManager::CachedProgramInfoManager() {
377}
378
379CachedProgramInfoManager::~CachedProgramInfoManager() {
380
381}
382
383CachedProgramInfoManager::Program*
384    CachedProgramInfoManager::GetProgramInfo(
385        GLES2Implementation* gl, GLuint program) {
386  lock_.AssertAcquired();
387  ProgramInfoMap::iterator it = program_infos_.find(program);
388  if (it == program_infos_.end()) {
389    return NULL;
390  }
391  Program* info = &it->second;
392  info->Update(gl, program);
393  return info;
394}
395
396void CachedProgramInfoManager::CreateInfo(GLuint program) {
397  base::AutoLock auto_lock(lock_);
398  program_infos_.erase(program);
399  std::pair<ProgramInfoMap::iterator, bool> result =
400      program_infos_.insert(std::make_pair(program, Program()));
401
402  DCHECK(result.second);
403}
404
405void CachedProgramInfoManager::DeleteInfo(GLuint program) {
406  base::AutoLock auto_lock(lock_);
407  program_infos_.erase(program);
408}
409
410bool CachedProgramInfoManager::GetProgramiv(
411    GLES2Implementation* gl, GLuint program, GLenum pname, GLint* params) {
412  base::AutoLock auto_lock(lock_);
413  Program* info = GetProgramInfo(gl, program);
414  if (!info) {
415    return false;
416  }
417  return info->GetProgramiv(pname, params);
418}
419
420GLint CachedProgramInfoManager::GetAttribLocation(
421    GLES2Implementation* gl, GLuint program, const char* name) {
422  base::AutoLock auto_lock(lock_);
423  Program* info = GetProgramInfo(gl, program);
424  if (info) {
425    return info->GetAttribLocation(name);
426  }
427  return gl->GetAttribLocationHelper(program, name);
428}
429
430GLint CachedProgramInfoManager::GetUniformLocation(
431    GLES2Implementation* gl, GLuint program, const char* name) {
432  base::AutoLock auto_lock(lock_);
433  Program* info = GetProgramInfo(gl, program);
434  if (info) {
435    return info->GetUniformLocation(name);
436  }
437  return gl->GetUniformLocationHelper(program, name);
438}
439
440bool CachedProgramInfoManager::GetActiveAttrib(
441    GLES2Implementation* gl,
442    GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
443    GLint* size, GLenum* type, char* name) {
444  base::AutoLock auto_lock(lock_);
445  Program* info = GetProgramInfo(gl, program);
446  if (info) {
447    const Program::VertexAttrib* attrib_info =
448        info->GetAttribInfo(index);
449    if (attrib_info) {
450      if (size) {
451        *size = attrib_info->size;
452      }
453      if (type) {
454        *type = attrib_info->type;
455      }
456      if (length || name) {
457        GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1,
458                                    std::max(static_cast<size_t>(0),
459                                             attrib_info->name.size()));
460        if (length) {
461          *length = max_size;
462        }
463        if (name && bufsize > 0) {
464          memcpy(name, attrib_info->name.c_str(), max_size);
465          name[max_size] = '\0';
466        }
467      }
468      return true;
469    }
470  }
471  return gl->GetActiveAttribHelper(
472      program, index, bufsize, length, size, type, name);
473}
474
475bool CachedProgramInfoManager::GetActiveUniform(
476    GLES2Implementation* gl,
477    GLuint program, GLuint index, GLsizei bufsize, GLsizei* length,
478    GLint* size, GLenum* type, char* name) {
479  base::AutoLock auto_lock(lock_);
480  Program* info = GetProgramInfo(gl, program);
481  if (info) {
482    const Program::UniformInfo* uniform_info = info->GetUniformInfo(index);
483    if (uniform_info) {
484      if (size) {
485        *size = uniform_info->size;
486      }
487      if (type) {
488        *type = uniform_info->type;
489      }
490      if (length || name) {
491        GLsizei max_size = std::min(static_cast<size_t>(bufsize) - 1,
492                                    std::max(static_cast<size_t>(0),
493                                             uniform_info->name.size()));
494        if (length) {
495          *length = max_size;
496        }
497        if (name && bufsize > 0) {
498          memcpy(name, uniform_info->name.c_str(), max_size);
499          name[max_size] = '\0';
500        }
501      }
502      return true;
503    }
504  }
505  return gl->GetActiveUniformHelper(
506      program, index, bufsize, length, size, type, name);
507}
508
509ProgramInfoManager::ProgramInfoManager() {
510}
511
512ProgramInfoManager::~ProgramInfoManager() {
513}
514
515ProgramInfoManager* ProgramInfoManager::Create(
516    bool shared_resources_across_processes) {
517  if (shared_resources_across_processes) {
518    return new NonCachedProgramInfoManager();
519  } else {
520    return new CachedProgramInfoManager();
521  }
522}
523
524}  // namespace gles2
525}  // namespace gpu
526
527