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