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/vertex_attrib_manager.h"
6
7#include <list>
8
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/strings/string_number_conversions.h"
12#include "build/build_config.h"
13#define GLES2_GPU_SERVICE 1
14#include "gpu/command_buffer/common/gles2_cmd_format.h"
15#include "gpu/command_buffer/common/gles2_cmd_utils.h"
16#include "gpu/command_buffer/service/buffer_manager.h"
17#include "gpu/command_buffer/service/error_state.h"
18#include "gpu/command_buffer/service/feature_info.h"
19#include "gpu/command_buffer/service/gl_utils.h"
20#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
21#include "gpu/command_buffer/service/gpu_switches.h"
22#include "gpu/command_buffer/service/program_manager.h"
23#include "gpu/command_buffer/service/vertex_array_manager.h"
24
25namespace gpu {
26namespace gles2 {
27
28VertexAttrib::VertexAttrib()
29    : index_(0),
30      enabled_(false),
31      size_(4),
32      type_(GL_FLOAT),
33      offset_(0),
34      normalized_(GL_FALSE),
35      gl_stride_(0),
36      real_stride_(16),
37      divisor_(0),
38      is_client_side_array_(false),
39      list_(NULL) {
40}
41
42VertexAttrib::~VertexAttrib() {
43}
44
45void VertexAttrib::SetInfo(
46    Buffer* buffer,
47    GLint size,
48    GLenum type,
49    GLboolean normalized,
50    GLsizei gl_stride,
51    GLsizei real_stride,
52    GLsizei offset) {
53  DCHECK_GT(real_stride, 0);
54  buffer_ = buffer;
55  size_ = size;
56  type_ = type;
57  normalized_ = normalized;
58  gl_stride_ = gl_stride;
59  real_stride_ = real_stride;
60  offset_ = offset;
61}
62
63void VertexAttrib::Unbind(Buffer* buffer) {
64  if (buffer_.get() == buffer) {
65    buffer_ = NULL;
66  }
67}
68
69bool VertexAttrib::CanAccess(GLuint index) const {
70  if (!enabled_) {
71    return true;
72  }
73
74  if (!buffer_.get() || buffer_->IsDeleted()) {
75    return false;
76  }
77
78  // The number of elements that can be accessed.
79  GLsizeiptr buffer_size = buffer_->size();
80  if (offset_ > buffer_size || real_stride_ == 0) {
81    return false;
82  }
83
84  uint32 usable_size = buffer_size - offset_;
85  GLuint num_elements = usable_size / real_stride_ +
86      ((usable_size % real_stride_) >=
87       (GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type_) * size_) ? 1 : 0);
88  return index < num_elements;
89}
90
91VertexAttribManager::VertexAttribManager()
92    : num_fixed_attribs_(0),
93      element_array_buffer_(NULL),
94      manager_(NULL),
95      deleted_(false),
96      service_id_(0) {
97}
98
99VertexAttribManager::VertexAttribManager(
100    VertexArrayManager* manager, GLuint service_id, uint32 num_vertex_attribs)
101    : num_fixed_attribs_(0),
102      element_array_buffer_(NULL),
103      manager_(manager),
104      deleted_(false),
105      service_id_(service_id) {
106  manager_->StartTracking(this);
107  Initialize(num_vertex_attribs, false);
108}
109
110VertexAttribManager::~VertexAttribManager() {
111  if (manager_) {
112    if (manager_->have_context_) {
113      if (service_id_ != 0)  // 0 indicates an emulated VAO
114        glDeleteVertexArraysOES(1, &service_id_);
115    }
116    manager_->StopTracking(this);
117    manager_ = NULL;
118  }
119}
120
121void VertexAttribManager::Initialize(
122    uint32 max_vertex_attribs, bool init_attribs) {
123  vertex_attribs_.resize(max_vertex_attribs);
124
125  for (uint32 vv = 0; vv < vertex_attribs_.size(); ++vv) {
126    vertex_attribs_[vv].set_index(vv);
127    vertex_attribs_[vv].SetList(&disabled_vertex_attribs_);
128
129    if (init_attribs) {
130      glVertexAttrib4f(vv, 0.0f, 0.0f, 0.0f, 1.0f);
131    }
132  }
133}
134
135void VertexAttribManager::SetElementArrayBuffer(Buffer* buffer) {
136  element_array_buffer_ = buffer;
137}
138
139bool VertexAttribManager::Enable(GLuint index, bool enable) {
140  if (index >= vertex_attribs_.size()) {
141    return false;
142  }
143  VertexAttrib& info = vertex_attribs_[index];
144  if (info.enabled() != enable) {
145    info.set_enabled(enable);
146    info.SetList(enable ? &enabled_vertex_attribs_ : &disabled_vertex_attribs_);
147  }
148  return true;
149}
150
151void VertexAttribManager::Unbind(Buffer* buffer) {
152  if (element_array_buffer_.get() == buffer) {
153    element_array_buffer_ = NULL;
154  }
155  for (uint32 vv = 0; vv < vertex_attribs_.size(); ++vv) {
156    vertex_attribs_[vv].Unbind(buffer);
157  }
158}
159
160bool VertexAttribManager::ValidateBindings(
161    const char* function_name,
162    GLES2Decoder* decoder,
163    FeatureInfo* feature_info,
164    Program* current_program,
165    GLuint max_vertex_accessed,
166    bool instanced,
167    GLsizei primcount) {
168  DCHECK(primcount);
169  ErrorState* error_state = decoder->GetErrorState();
170  // true if any enabled, used divisor is zero
171  bool divisor0 = false;
172  bool have_enabled_active_attribs = false;
173  const GLuint kInitialBufferId = 0xFFFFFFFFU;
174  GLuint current_buffer_id = kInitialBufferId;
175  bool use_client_side_arrays_for_stream_buffers = feature_info->workarounds(
176      ).use_client_side_arrays_for_stream_buffers;
177  // Validate all attribs currently enabled. If they are used by the current
178  // program then check that they have enough elements to handle the draw call.
179  // If they are not used by the current program check that they have a buffer
180  // assigned.
181  for (VertexAttribList::iterator it = enabled_vertex_attribs_.begin();
182       it != enabled_vertex_attribs_.end(); ++it) {
183    VertexAttrib* attrib = *it;
184    const Program::VertexAttrib* attrib_info =
185        current_program->GetAttribInfoByLocation(attrib->index());
186    if (attrib_info) {
187      divisor0 |= (attrib->divisor() == 0);
188      have_enabled_active_attribs = true;
189      GLuint count = attrib->MaxVertexAccessed(primcount, max_vertex_accessed);
190      // This attrib is used in the current program.
191      if (!attrib->CanAccess(count)) {
192        ERRORSTATE_SET_GL_ERROR(
193            error_state, GL_INVALID_OPERATION, function_name,
194            (std::string(
195                 "attempt to access out of range vertices in attribute ") +
196             base::IntToString(attrib->index())).c_str());
197        return false;
198      }
199      if (use_client_side_arrays_for_stream_buffers) {
200        Buffer* buffer = attrib->buffer();
201        glEnableVertexAttribArray(attrib->index());
202        if (buffer->IsClientSideArray()) {
203          if (current_buffer_id != 0) {
204            current_buffer_id = 0;
205            glBindBuffer(GL_ARRAY_BUFFER, 0);
206          }
207          attrib->set_is_client_side_array(true);
208          const void* ptr = buffer->GetRange(attrib->offset(), 0);
209          DCHECK(ptr);
210          glVertexAttribPointer(
211              attrib->index(),
212              attrib->size(),
213              attrib->type(),
214              attrib->normalized(),
215              attrib->gl_stride(),
216              ptr);
217        } else if (attrib->is_client_side_array()) {
218          attrib->set_is_client_side_array(false);
219          GLuint new_buffer_id = buffer->service_id();
220          if (new_buffer_id != current_buffer_id) {
221            current_buffer_id = new_buffer_id;
222            glBindBuffer(GL_ARRAY_BUFFER, current_buffer_id);
223          }
224          const void* ptr = reinterpret_cast<const void*>(attrib->offset());
225          glVertexAttribPointer(
226              attrib->index(),
227              attrib->size(),
228              attrib->type(),
229              attrib->normalized(),
230              attrib->gl_stride(),
231              ptr);
232        }
233      }
234    } else {
235      // This attrib is not used in the current program.
236      if (!attrib->buffer()) {
237        ERRORSTATE_SET_GL_ERROR(
238            error_state, GL_INVALID_OPERATION, function_name,
239            (std::string(
240                 "attempt to render with no buffer attached to "
241                 "enabled attribute ") +
242                 base::IntToString(attrib->index())).c_str());
243        return false;
244      } else if (use_client_side_arrays_for_stream_buffers) {
245        Buffer* buffer = attrib->buffer();
246        // Disable client side arrays for unused attributes else we'll
247        // read bad memory
248        if (buffer->IsClientSideArray()) {
249          // Don't disable attrib 0 since it's special.
250          if (attrib->index() > 0) {
251            glDisableVertexAttribArray(attrib->index());
252          }
253        }
254      }
255    }
256  }
257
258  // Instanced drawing needs at least one enabled attribute with divisor zero.
259  // Non-instanced drawing is fine with having no attributes at all, but if
260  // there are attributes, at least one should have divisor zero.
261  // (See ANGLE_instanced_arrays spec)
262  if (!divisor0 && (instanced || have_enabled_active_attribs)) {
263    ERRORSTATE_SET_GL_ERROR(
264        error_state, GL_INVALID_OPERATION, function_name,
265        "attempt to draw with all attributes having non-zero divisors");
266    return false;
267  }
268
269  if (current_buffer_id != kInitialBufferId) {
270    // Restore the buffer binding.
271    decoder->RestoreBufferBindings();
272  }
273
274  return true;
275}
276
277}  // namespace gles2
278}  // namespace gpu
279