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