• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 namespace gpu {
26 namespace gles2 {
27 
VertexAttrib()28 VertexAttrib::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 
~VertexAttrib()42 VertexAttrib::~VertexAttrib() {
43 }
44 
SetInfo(Buffer * buffer,GLint size,GLenum type,GLboolean normalized,GLsizei gl_stride,GLsizei real_stride,GLsizei offset)45 void 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 
Unbind(Buffer * buffer)63 void VertexAttrib::Unbind(Buffer* buffer) {
64   if (buffer_.get() == buffer) {
65     buffer_ = NULL;
66   }
67 }
68 
CanAccess(GLuint index) const69 bool 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 
VertexAttribManager()91 VertexAttribManager::VertexAttribManager()
92     : num_fixed_attribs_(0),
93       element_array_buffer_(NULL),
94       manager_(NULL),
95       deleted_(false),
96       service_id_(0) {
97 }
98 
VertexAttribManager(VertexArrayManager * manager,GLuint service_id,uint32 num_vertex_attribs)99 VertexAttribManager::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 
~VertexAttribManager()110 VertexAttribManager::~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 
Initialize(uint32 max_vertex_attribs,bool init_attribs)121 void 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 
SetElementArrayBuffer(Buffer * buffer)135 void VertexAttribManager::SetElementArrayBuffer(Buffer* buffer) {
136   element_array_buffer_ = buffer;
137 }
138 
Enable(GLuint index,bool enable)139 bool 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 
Unbind(Buffer * buffer)151 void 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 
ValidateBindings(const char * function_name,GLES2Decoder * decoder,FeatureInfo * feature_info,Program * current_program,GLuint max_vertex_accessed,bool instanced,GLsizei primcount)160 bool 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