azul_glium/
vertex_array_object.rs

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
21/// Stores and handles vertex attributes.
22pub struct VertexAttributesSystem {
23    // we maintain a list of VAOs for each vertexbuffer-indexbuffer-program association
24    // the key is a (buffers-list-with-offset, program) ; the buffers list must be sorted
25    vaos: RefCell<HashMap<(Vec<(gl::types::GLuint, usize)>, Handle), VertexArrayObject>>,
26}
27
28/// Object allowing one to bind vertex attributes to the current context.
29pub 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    /// Builds a new `VertexAttributesSystem`.
39    #[inline]
40    pub fn new() -> VertexAttributesSystem {
41        VertexAttributesSystem {
42            vaos: RefCell::new(HashMap::with_hasher(Default::default())),
43        }
44    }
45
46    /// Starts the process of binding vertex attributes.
47    ///
48    /// `base_vertex` should be set to true if the backend supports the `glDraw*BaseVertex`
49    /// functions. If `base_vertex` is true, then `bind` will return the base vertex to use.
50    #[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    /// This function *must* be called whenever you destroy a buffer so that the system can
69    /// purge its VAOs cache.
70    #[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    /// This function *must* be called whenever you destroy a program so that the system can
78    /// purge its VAOs cache.
79    #[inline]
80    pub fn purge_program(ctxt: &mut CommandContext, program: Handle) {
81        VertexAttributesSystem::purge_if(ctxt, |&(_, p)| p == program)
82    }
83
84    /// Purges the VAOs cache.
85    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    /// Purges the VAOs cache. Contrary to `purge_all`, this function expects the system to be
95    /// destroyed soon.
96    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    /// Tells the VAOs system that the currently bound element array buffer will change.
106    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    /// Purges VAOs that match a certain condition.
118    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    /// Adds a buffer to bind as a source of vertices.
138    ///
139    /// # Parameters
140    ///
141    /// - `buffer`: The buffer to bind.
142    /// - `first`: Offset of the first element of the buffer in number of elements.
143    /// - `divisor`: If `Some`, use this value for `glVertexAttribDivisor` (instancing-related).
144    #[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    /// Finish binding the vertex attributes.
160    ///
161    /// If `base_vertex` was set to true, returns the base vertex to use when drawing.
162    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            // VAOs are supported
170
171            // finding the base vertex
172            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            // removing the offset corresponding to the base vertex
182            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            // trying to find an existing VAO in the cache
197            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            // if not found, building a new one
205            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            // VAOs are not supported
217
218            // just in case
219            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            // TODO: it is unlikely that a backend supports base vertex but not VAOs, so we just
233            //       ignore this case ; however it would ideally be better to handle it
234            if self.base_vertex {
235                Some(0)
236            } else {
237                None
238            }
239        }
240    }
241}
242
243/// Stores informations about how to bind a vertex buffer, an index buffer and a program.
244struct 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    /// Builds a new `VertexArrayObject`.
253    ///
254    /// The vertex buffer, index buffer and program must not outlive the
255    /// VAO, and the VB & program attributes must not change.
256    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        // checking the attributes types
261        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        // checking for missing attributes
278        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        // TODO: check for collisions between the vertices sources
292
293        // building the VAO
294        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        // we don't use DSA as we're going to make multiple calls for this VAO
312        // and we're likely going to use the VAO right after it's been created
313        bind_vao(&mut ctxt, id);
314
315        // binding index buffer
316        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    /// Sets this VAO as the current VAO.
333    fn bind(&self, ctxt: &mut CommandContext) {
334        unsafe {
335            bind_vao(ctxt, self.id);
336
337            if self.element_array_buffer_hijacked.get() {
338                // TODO: use a proper function
339                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    /// Must be called to destroy the VAO (otherwise its destructor will panic as a safety
355    /// measure).
356    fn destroy(mut self, ctxt: &mut CommandContext) {
357        self.destroyed = true;
358
359        // unbinding
360        if ctxt.state.vertex_array == self.id {
361            ctxt.state.vertex_array = 0;
362        }
363
364        // deleting
365        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
478/// Binds the vertex array object as the current one. Unbinds if `0` is passed.
479///
480/// ## Panic
481///
482/// Panics if the backend doesn't support vertex array objects.
483fn 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
502/// Binds an individual attribute to the current VAO.
503unsafe 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    // glVertexAttribPointer uses the current array buffer
508    // TODO: use a proper function
509    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    // binding attributes
523    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}