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.
5 #include "gpu/command_buffer/service/vertex_attrib_manager.h"
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"
28 VertexAttrib::VertexAttrib()
34 normalized_(GL_FALSE
),
38 is_client_side_array_(false),
42 VertexAttrib::~VertexAttrib() {
45 void VertexAttrib::SetInfo(
53 DCHECK_GT(real_stride
, 0);
57 normalized_
= normalized
;
58 gl_stride_
= gl_stride
;
59 real_stride_
= real_stride
;
63 void VertexAttrib::Unbind(Buffer
* buffer
) {
64 if (buffer_
.get() == buffer
) {
69 bool VertexAttrib::CanAccess(GLuint index
) const {
74 if (!buffer_
.get() || buffer_
->IsDeleted()) {
78 // The number of elements that can be accessed.
79 GLsizeiptr buffer_size
= buffer_
->size();
80 if (offset_
> buffer_size
|| real_stride_
== 0) {
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
;
91 VertexAttribManager::VertexAttribManager()
92 : num_fixed_attribs_(0),
93 element_array_buffer_(NULL
),
99 VertexAttribManager::VertexAttribManager(
100 VertexArrayManager
* manager
, GLuint service_id
, uint32 num_vertex_attribs
)
101 : num_fixed_attribs_(0),
102 element_array_buffer_(NULL
),
105 service_id_(service_id
) {
106 manager_
->StartTracking(this);
107 Initialize(num_vertex_attribs
, false);
110 VertexAttribManager::~VertexAttribManager() {
112 if (manager_
->have_context_
) {
113 if (service_id_
!= 0) // 0 indicates an emulated VAO
114 glDeleteVertexArraysOES(1, &service_id_
);
116 manager_
->StopTracking(this);
121 void VertexAttribManager::Initialize(
122 uint32 max_vertex_attribs
, bool init_attribs
) {
123 vertex_attribs_
.resize(max_vertex_attribs
);
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_
);
130 glVertexAttrib4f(vv
, 0.0f
, 0.0f
, 0.0f
, 1.0f
);
135 void VertexAttribManager::SetElementArrayBuffer(Buffer
* buffer
) {
136 element_array_buffer_
= buffer
;
139 bool VertexAttribManager::Enable(GLuint index
, bool enable
) {
140 if (index
>= vertex_attribs_
.size()) {
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_
);
151 void VertexAttribManager::Unbind(Buffer
* buffer
) {
152 if (element_array_buffer_
.get() == buffer
) {
153 element_array_buffer_
= NULL
;
155 for (uint32 vv
= 0; vv
< vertex_attribs_
.size(); ++vv
) {
156 vertex_attribs_
[vv
].Unbind(buffer
);
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
,
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
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());
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
,
195 "attempt to access out of range vertices in attribute ") +
196 base::IntToString(attrib
->index())).c_str());
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);
207 attrib
->set_is_client_side_array(true);
208 const void* ptr
= buffer
->GetRange(attrib
->offset(), 0);
210 glVertexAttribPointer(
214 attrib
->normalized(),
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
);
224 const void* ptr
= reinterpret_cast<const void*>(attrib
->offset());
225 glVertexAttribPointer(
229 attrib
->normalized(),
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
,
240 "attempt to render with no buffer attached to "
241 "enabled attribute ") +
242 base::IntToString(attrib
->index())).c_str());
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
248 if (buffer
->IsClientSideArray()) {
249 // Don't disable attrib 0 since it's special.
250 if (attrib
->index() > 0) {
251 glDisableVertexAttribArray(attrib
->index());
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");
269 if (current_buffer_id
!= kInitialBufferId
) {
270 // Restore the buffer binding.
271 decoder
->RestoreBufferBindings();