1use std::borrow::Borrow;
2use std::cell::{Cell, RefCell};
3use std::collections::HashMap;
4use std::mem;
5
6use smallvec::SmallVec;
7
8use Handle;
9use buffer::BufferAnySlice;
10use program::Program;
11use vertex::AttributeType;
12use vertex::VertexFormat;
13use GlObject;
14use BufferExt;
15
16use gl;
17use context::CommandContext;
18use version::Api;
19use version::Version;
20
21pub struct VertexAttributesSystem {
23 vaos: RefCell<HashMap<(Vec<(gl::types::GLuint, usize)>, Handle), VertexArrayObject>>,
26}
27
28pub struct Binder<'a, 'b, 'c: 'b> {
30 context: &'b mut CommandContext<'c>,
31 program: &'a Program,
32 element_array_buffer: Option<BufferAnySlice<'a>>,
33 vertex_buffers: SmallVec<[(gl::types::GLuint, VertexFormat, usize, usize, Option<u32>); 2]>,
34 base_vertex: bool,
35}
36
37impl VertexAttributesSystem {
38 #[inline]
40 pub fn new() -> VertexAttributesSystem {
41 VertexAttributesSystem {
42 vaos: RefCell::new(HashMap::with_hasher(Default::default())),
43 }
44 }
45
46 #[inline]
51 pub fn start<'a, 'b, 'c: 'b>(ctxt: &'b mut CommandContext<'c>, program: &'a Program,
52 indices: Option<BufferAnySlice<'a>>, base_vertex: bool)
53 -> Binder<'a, 'b, 'c>
54 {
55 if let Some(indices) = indices {
56 indices.prepare_for_element_array(ctxt);
57 }
58
59 Binder {
60 context: ctxt,
61 program: program,
62 element_array_buffer: indices,
63 vertex_buffers: SmallVec::new(),
64 base_vertex: base_vertex,
65 }
66 }
67
68 #[inline]
71 pub fn purge_buffer(ctxt: &mut CommandContext, id: gl::types::GLuint) {
72 VertexAttributesSystem::purge_if(ctxt, |&(ref buffers, _)| {
73 buffers.iter().find(|&&(b, _)| b == id).is_some()
74 })
75 }
76
77 #[inline]
80 pub fn purge_program(ctxt: &mut CommandContext, program: Handle) {
81 VertexAttributesSystem::purge_if(ctxt, |&(_, p)| p == program)
82 }
83
84 pub fn purge_all(ctxt: &mut CommandContext) {
86 let vaos = mem::replace(&mut *ctxt.vertex_array_objects.vaos.borrow_mut(),
87 HashMap::with_hasher(Default::default()));
88
89 for (_, vao) in vaos {
90 vao.destroy(ctxt);
91 }
92 }
93
94 pub fn cleanup(ctxt: &mut CommandContext) {
97 let vaos = mem::replace(&mut *ctxt.vertex_array_objects.vaos.borrow_mut(),
98 HashMap::with_hasher(Default::default()));
99
100 for (_, vao) in vaos {
101 vao.destroy(ctxt);
102 }
103 }
104
105 pub fn hijack_current_element_array_buffer(ctxt: &mut CommandContext) {
107 let vaos = ctxt.vertex_array_objects.vaos.borrow_mut();
108
109 for (_, vao) in vaos.iter() {
110 if vao.id == ctxt.state.vertex_array {
111 vao.element_array_buffer_hijacked.set(true);
112 return;
113 }
114 }
115 }
116
117 fn purge_if<F>(ctxt: &mut CommandContext, mut condition: F)
119 where F: FnMut(&(Vec<(gl::types::GLuint, usize)>, Handle)) -> bool
120 {
121 let mut vaos = ctxt.vertex_array_objects.vaos.borrow_mut();
122
123 let mut keys = Vec::with_capacity(4);
124 for (key, _) in &*vaos {
125 if condition(key) {
126 keys.push(key.clone());
127 }
128 }
129
130 for key in keys {
131 vaos.remove(&key).unwrap().destroy(ctxt);
132 }
133 }
134}
135
136impl<'a, 'b, 'c> Binder<'a, 'b, 'c> {
137 #[inline]
145 pub fn add(mut self, buffer: &BufferAnySlice, bindings: &VertexFormat, divisor: Option<u32>)
146 -> Binder<'a, 'b, 'c>
147 {
148 let offset = buffer.get_offset_bytes();
149
150 buffer.prepare_for_vertex_attrib_array(self.context);
151
152 let (buffer, format, stride) = (buffer.get_id(), bindings.clone(),
153 buffer.get_elements_size());
154
155 self.vertex_buffers.push((buffer, format, offset, stride, divisor));
156 self
157 }
158
159 pub fn bind(mut self) -> Option<gl::types::GLint> {
163 let ctxt = self.context;
164
165 if ctxt.version >= &Version(Api::Gl, 3, 0) || ctxt.version >= &Version(Api::GlEs, 3, 0) ||
166 ctxt.extensions.gl_arb_vertex_array_object || ctxt.extensions.gl_oes_vertex_array_object
167 || ctxt.extensions.gl_apple_vertex_array_object
168 {
169 let base_vertex = if self.base_vertex {
173 Some(self.vertex_buffers.iter()
174 .filter(|&&(_, _, _, _, div)| div.is_none())
175 .map(|&(_, _, off, stride, _)| off / stride)
176 .min().unwrap_or(0))
177 } else {
178 None
179 };
180
181 if let Some(base_vertex) = base_vertex {
183 for &mut (_, _, ref mut off, stride, _) in self.vertex_buffers.iter_mut() {
184 *off -= base_vertex * stride;
185 }
186 }
187
188 let mut buffers_list: Vec<_> = self.vertex_buffers.iter()
189 .map(|&(v, _, o, s, _)| (v, o))
190 .collect();
191 buffers_list.push((self.element_array_buffer.map(|b| b.get_id()).unwrap_or(0), 0));
192 buffers_list.sort();
193
194 let program_id = self.program.get_id();
195
196 if let Some(value) = ctxt.vertex_array_objects.vaos.borrow_mut()
198 .get(&(buffers_list.clone(), program_id))
199 {
200 value.bind(ctxt);
201 return base_vertex.map(|v| v as gl::types::GLint);
202 }
203
204 let new_vao = unsafe {
206 VertexArrayObject::new(ctxt, &self.vertex_buffers,
207 self.element_array_buffer, self.program)
208 };
209
210 new_vao.bind(ctxt);
211 ctxt.vertex_array_objects.vaos.borrow_mut().insert((buffers_list, program_id), new_vao);
212
213 base_vertex.map(|v| v as gl::types::GLint)
214
215 } else {
216 bind_vao(ctxt, 0);
220
221 if let Some(element_array_buffer) = self.element_array_buffer {
222 element_array_buffer.bind_to_element_array(ctxt);
223 }
224
225 for (vertex_buffer, bindings, offset, stride, divisor) in self.vertex_buffers.into_iter() {
226 unsafe {
227 bind_attribute(ctxt, self.program, vertex_buffer, &bindings, offset, stride,
228 divisor);
229 }
230 }
231
232 if self.base_vertex {
235 Some(0)
236 } else {
237 None
238 }
239 }
240 }
241}
242
243struct VertexArrayObject {
245 id: gl::types::GLuint,
246 destroyed: bool,
247 element_array_buffer: gl::types::GLuint,
248 element_array_buffer_hijacked: Cell<bool>,
249}
250
251impl VertexArrayObject {
252 unsafe fn new(mut ctxt: &mut CommandContext,
257 vertex_buffers: &[(gl::types::GLuint, VertexFormat, usize, usize, Option<u32>)],
258 index_buffer: Option<BufferAnySlice>, program: &Program) -> VertexArrayObject
259 {
260 for &(_, ref bindings, _, _, _) in vertex_buffers {
262 for &(ref name, _, ty, _) in bindings.iter() {
263 let attribute = match program.get_attribute(Borrow::<str>::borrow(name)) {
264 Some(a) => a,
265 None => continue
266 };
267
268 if ty.get_num_components() != attribute.ty.get_num_components() ||
269 attribute.size != 1
270 {
271 panic!("The program attribute `{}` does not match the vertex format. \
272 Program expected {:?}, got {:?}.", name, attribute.ty, ty);
273 }
274 }
275 }
276
277 for (&ref name, _) in program.attributes() {
279 let mut found = false;
280 for &(_, ref bindings, _, _, _) in vertex_buffers {
281 if bindings.iter().find(|&&(ref n, _, _, _)| n == name).is_some() {
282 found = true;
283 break;
284 }
285 }
286 if !found {
287 panic!("The program attribute `{}` is missing in the vertex bindings", name);
288 }
289 };
290
291 let id = {
295 let mut id = mem::uninitialized();
296 if ctxt.version >= &Version(Api::Gl, 3, 0) ||
297 ctxt.version >= &Version(Api::GlEs, 3, 0) ||
298 ctxt.extensions.gl_arb_vertex_array_object
299 {
300 ctxt.gl.GenVertexArrays(1, &mut id);
301 } else if ctxt.extensions.gl_oes_vertex_array_object {
302 ctxt.gl.GenVertexArraysOES(1, &mut id);
303 } else if ctxt.extensions.gl_apple_vertex_array_object {
304 ctxt.gl.GenVertexArraysAPPLE(1, &mut id);
305 } else {
306 unreachable!();
307 };
308 id
309 };
310
311 bind_vao(&mut ctxt, id);
314
315 if let Some(index_buffer) = index_buffer {
317 index_buffer.bind_to_element_array(&mut ctxt);
318 }
319
320 for &(vertex_buffer, ref bindings, offset, stride, divisor) in vertex_buffers {
321 bind_attribute(ctxt, program, vertex_buffer, bindings, offset, stride, divisor);
322 }
323
324 VertexArrayObject {
325 id: id,
326 destroyed: false,
327 element_array_buffer: index_buffer.map(|b| b.get_id()).unwrap_or(0),
328 element_array_buffer_hijacked: Cell::new(false),
329 }
330 }
331
332 fn bind(&self, ctxt: &mut CommandContext) {
334 unsafe {
335 bind_vao(ctxt, self.id);
336
337 if self.element_array_buffer_hijacked.get() {
338 if ctxt.version >= &Version(Api::Gl, 1, 5) ||
340 ctxt.version >= &Version(Api::GlEs, 2, 0)
341 {
342 ctxt.gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.element_array_buffer);
343 } else if ctxt.extensions.gl_arb_vertex_buffer_object {
344 ctxt.gl.BindBufferARB(gl::ELEMENT_ARRAY_BUFFER_ARB, self.element_array_buffer);
345 } else {
346 unreachable!();
347 }
348
349 self.element_array_buffer_hijacked.set(false);
350 }
351 }
352 }
353
354 fn destroy(mut self, ctxt: &mut CommandContext) {
357 self.destroyed = true;
358
359 if ctxt.state.vertex_array == self.id {
361 ctxt.state.vertex_array = 0;
362 }
363
364 if ctxt.version >= &Version(Api::Gl, 3, 0) ||
366 ctxt.version >= &Version(Api::GlEs, 3, 0) ||
367 ctxt.extensions.gl_arb_vertex_array_object
368 {
369 unsafe { ctxt.gl.DeleteVertexArrays(1, [ self.id ].as_ptr()) };
370 } else if ctxt.extensions.gl_oes_vertex_array_object {
371 unsafe { ctxt.gl.DeleteVertexArraysOES(1, [ self.id ].as_ptr()) };
372 } else if ctxt.extensions.gl_apple_vertex_array_object {
373 unsafe { ctxt.gl.DeleteVertexArraysAPPLE(1, [ self.id ].as_ptr()) };
374 } else {
375 unreachable!();
376 }
377 }
378}
379
380impl Drop for VertexArrayObject {
381 #[inline]
382 fn drop(&mut self) {
383 assert!(self.destroyed);
384 }
385}
386
387impl GlObject for VertexArrayObject {
388 type Id = gl::types::GLuint;
389
390 #[inline]
391 fn get_id(&self) -> gl::types::GLuint {
392 self.id
393 }
394}
395
396fn vertex_binding_type_to_gl(ty: AttributeType) -> (gl::types::GLenum, gl::types::GLint, gl::types::GLint) {
397 match ty {
398 AttributeType::I8 => (gl::BYTE, 1, 1),
399 AttributeType::I8I8 => (gl::BYTE, 2, 1),
400 AttributeType::I8I8I8 => (gl::BYTE, 3, 1),
401 AttributeType::I8I8I8I8 => (gl::BYTE, 4, 1),
402 AttributeType::U8 => (gl::UNSIGNED_BYTE, 1, 1),
403 AttributeType::U8U8 => (gl::UNSIGNED_BYTE, 2, 1),
404 AttributeType::U8U8U8 => (gl::UNSIGNED_BYTE, 3, 1),
405 AttributeType::U8U8U8U8 => (gl::UNSIGNED_BYTE, 4, 1),
406 AttributeType::I16 => (gl::SHORT, 1, 1),
407 AttributeType::I16I16 => (gl::SHORT, 2, 1),
408 AttributeType::I16I16I16 => (gl::SHORT, 3, 1),
409 AttributeType::I16I16I16I16 => (gl::SHORT, 4, 1),
410 AttributeType::U16 => (gl::UNSIGNED_SHORT, 1, 1),
411 AttributeType::U16U16 => (gl::UNSIGNED_SHORT, 2, 1),
412 AttributeType::U16U16U16 => (gl::UNSIGNED_SHORT, 3, 1),
413 AttributeType::U16U16U16U16 => (gl::UNSIGNED_SHORT, 4, 1),
414 AttributeType::I32 => (gl::INT, 1, 1),
415 AttributeType::I32I32 => (gl::INT, 2, 1),
416 AttributeType::I32I32I32 => (gl::INT, 3, 1),
417 AttributeType::I32I32I32I32 => (gl::INT, 4, 1),
418 AttributeType::U32 => (gl::UNSIGNED_INT, 1, 1),
419 AttributeType::U32U32 => (gl::UNSIGNED_INT, 2, 1),
420 AttributeType::U32U32U32 => (gl::UNSIGNED_INT, 3, 1),
421 AttributeType::U32U32U32U32 => (gl::UNSIGNED_INT, 4, 1),
422 AttributeType::I64 => (gl::INT64_NV, 1, 1),
423 AttributeType::I64I64 => (gl::INT64_NV, 2, 1),
424 AttributeType::I64I64I64 => (gl::INT64_NV, 3, 1),
425 AttributeType::I64I64I64I64 => (gl::INT64_NV, 4, 1),
426 AttributeType::U64 => (gl::UNSIGNED_INT64_NV, 1, 1),
427 AttributeType::U64U64 => (gl::UNSIGNED_INT64_NV, 2, 1),
428 AttributeType::U64U64U64 => (gl::UNSIGNED_INT64_NV, 3, 1),
429 AttributeType::U64U64U64U64 => (gl::UNSIGNED_INT64_NV, 4, 1),
430 AttributeType::F16 => (gl::HALF_FLOAT, 1, 1),
431 AttributeType::F16F16 => (gl::HALF_FLOAT, 2, 1),
432 AttributeType::F16F16F16 => (gl::HALF_FLOAT, 3, 1),
433 AttributeType::F16F16F16F16 => (gl::HALF_FLOAT, 4, 1),
434 AttributeType::F16x2x2 => (gl::HALF_FLOAT, 2, 2),
435 AttributeType::F16x2x3 => (gl::HALF_FLOAT, 2, 3),
436 AttributeType::F16x2x4 => (gl::HALF_FLOAT, 2, 4),
437 AttributeType::F16x3x2 => (gl::HALF_FLOAT, 3, 2),
438 AttributeType::F16x3x3 => (gl::HALF_FLOAT, 3, 3),
439 AttributeType::F16x3x4 => (gl::HALF_FLOAT, 3, 4),
440 AttributeType::F16x4x2 => (gl::HALF_FLOAT, 4, 2),
441 AttributeType::F16x4x3 => (gl::HALF_FLOAT, 4, 3),
442 AttributeType::F16x4x4 => (gl::HALF_FLOAT, 4, 4),
443 AttributeType::F32 => (gl::FLOAT, 1, 1),
444 AttributeType::F32F32 => (gl::FLOAT, 2, 1),
445 AttributeType::F32F32F32 => (gl::FLOAT, 3, 1),
446 AttributeType::F32F32F32F32 => (gl::FLOAT, 4, 1),
447 AttributeType::F32x2x2 => (gl::FLOAT, 2, 2),
448 AttributeType::F32x2x3 => (gl::FLOAT, 2, 3),
449 AttributeType::F32x2x4 => (gl::FLOAT, 2, 4),
450 AttributeType::F32x3x2 => (gl::FLOAT, 3, 2),
451 AttributeType::F32x3x3 => (gl::FLOAT, 3, 3),
452 AttributeType::F32x3x4 => (gl::FLOAT, 3, 4),
453 AttributeType::F32x4x2 => (gl::FLOAT, 4, 2),
454 AttributeType::F32x4x3 => (gl::FLOAT, 4, 3),
455 AttributeType::F32x4x4 => (gl::FLOAT, 4, 4),
456 AttributeType::F64 => (gl::DOUBLE, 1, 1),
457 AttributeType::F64F64 => (gl::DOUBLE, 2, 1),
458 AttributeType::F64F64F64 => (gl::DOUBLE, 3, 1),
459 AttributeType::F64F64F64F64 => (gl::DOUBLE, 4, 1),
460 AttributeType::F64x2x2 => (gl::DOUBLE, 2, 2),
461 AttributeType::F64x2x3 => (gl::DOUBLE, 2, 3),
462 AttributeType::F64x2x4 => (gl::DOUBLE, 2, 4),
463 AttributeType::F64x3x2 => (gl::DOUBLE, 3, 2),
464 AttributeType::F64x3x3 => (gl::DOUBLE, 3, 3),
465 AttributeType::F64x3x4 => (gl::DOUBLE, 3, 4),
466 AttributeType::F64x4x2 => (gl::DOUBLE, 4, 2),
467 AttributeType::F64x4x3 => (gl::DOUBLE, 4, 3),
468 AttributeType::F64x4x4 => (gl::DOUBLE, 4, 4),
469 AttributeType::I2I10I10I10Reversed => (gl::INT_2_10_10_10_REV, 1, 1),
470 AttributeType::U2U10U10U10Reversed => (gl::UNSIGNED_INT_2_10_10_10_REV, 1, 1),
471 AttributeType::I10I10I10I2 => (gl::INT_10_10_10_2_OES, 1, 1),
472 AttributeType::U10U10U10U2 => (gl::UNSIGNED_INT_10_10_10_2_OES, 1, 1),
473 AttributeType::F10F11F11UnsignedIntReversed => (gl::UNSIGNED_INT_10F_11F_11F_REV, 1, 1),
474 AttributeType::FixedFloatI16U16 => (gl::FIXED, 1, 1),
475 }
476}
477
478fn bind_vao(ctxt: &mut CommandContext, vao_id: gl::types::GLuint) {
484 if ctxt.state.vertex_array != vao_id {
485 if ctxt.version >= &Version(Api::Gl, 3, 0) ||
486 ctxt.version >= &Version(Api::GlEs, 3, 0) ||
487 ctxt.extensions.gl_arb_vertex_array_object
488 {
489 unsafe { ctxt.gl.BindVertexArray(vao_id) };
490 } else if ctxt.extensions.gl_oes_vertex_array_object {
491 unsafe { ctxt.gl.BindVertexArrayOES(vao_id) };
492 } else if ctxt.extensions.gl_apple_vertex_array_object {
493 unsafe { ctxt.gl.BindVertexArrayAPPLE(vao_id) };
494 } else {
495 unreachable!();
496 }
497
498 ctxt.state.vertex_array = vao_id;
499 }
500}
501
502unsafe fn bind_attribute(ctxt: &mut CommandContext, program: &Program,
504 vertex_buffer: gl::types::GLuint, bindings: &VertexFormat,
505 buffer_offset: usize, stride: usize, divisor: Option<u32>)
506{
507 if ctxt.state.array_buffer_binding != vertex_buffer {
510 if ctxt.version >= &Version(Api::Gl, 1, 5) ||
511 ctxt.version >= &Version(Api::GlEs, 2, 0)
512 {
513 ctxt.gl.BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
514 } else if ctxt.extensions.gl_arb_vertex_buffer_object {
515 ctxt.gl.BindBufferARB(gl::ARRAY_BUFFER_ARB, vertex_buffer);
516 } else {
517 unreachable!();
518 }
519 ctxt.state.array_buffer_binding = vertex_buffer;
520 }
521
522 for &(ref name, offset, ty, normalize) in bindings.iter() {
524 let (data_type, elements_count, instances_count) = vertex_binding_type_to_gl(ty);
525
526 let attribute = match program.get_attribute(Borrow::<str>::borrow(name)) {
527 Some(a) => a,
528 None => continue
529 };
530
531 if attribute.location != -1 {
532 let (attribute_ty, _, _) = vertex_binding_type_to_gl(attribute.ty);
533 if normalize {
534 for i in 0..instances_count {
535 ctxt.gl.VertexAttribPointer((attribute.location + i) as u32,
536 elements_count as gl::types::GLint, data_type, 1,
537 stride as i32,
538 (buffer_offset + offset + (i * elements_count * 4) as usize) as *const _)
539 }
540 } else {
541 match attribute_ty {
542 gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT |
543 gl::INT | gl::UNSIGNED_INT =>
544 ctxt.gl.VertexAttribIPointer(attribute.location as u32,
545 elements_count as gl::types::GLint, data_type,
546 stride as i32,
547 (buffer_offset + offset) as *const _),
548
549 gl::FLOAT => {
550 for i in 0..instances_count {
551 ctxt.gl.VertexAttribPointer((attribute.location + i) as u32,
552 elements_count as gl::types::GLint, data_type, 0,
553 stride as i32,
554 (buffer_offset + offset + (i * elements_count * 4) as usize) as *const _)
555 }
556 },
557
558 gl::DOUBLE | gl::INT64_NV | gl::UNSIGNED_INT64_NV => {
559 for i in 0..instances_count {
560 ctxt.gl.VertexAttribLPointer((attribute.location + i) as u32,
561 elements_count as gl::types::GLint, data_type,
562 stride as i32,
563 (buffer_offset + offset + (i * elements_count * 8) as usize) as *const _)
564 }
565 },
566
567 _ => unreachable!()
568 }
569 }
570
571 for i in 0..instances_count {
572 if let Some(divisor) = divisor {
573 ctxt.gl.VertexAttribDivisor((attribute.location + i) as u32, divisor);
574 }
575 ctxt.gl.EnableVertexAttribArray((attribute.location + i) as u32);
576 }
577 }
578 }
579}