glium/program/
reflection.rs

1use crate::gl;
2
3use std::cmp;
4use std::collections::HashMap;
5use std::hash::BuildHasherDefault;
6use std::ffi;
7use std::ptr;
8use std::os::raw;
9
10use fnv::FnvHasher;
11
12use crate::context::CommandContext;
13use crate::version::Version;
14use crate::version::Api;
15
16use crate::uniforms::UniformType;
17use crate::vertex::AttributeType;
18use crate::program;
19
20use crate::Handle;
21
22/// Information about a uniform (except its name).
23#[derive(Debug, Copy, Clone)]
24pub struct Uniform {
25    /// The location of the uniform.
26    ///
27    /// This is internal information, you probably don't need to use it.
28    pub location: i32,
29
30    /// Type of the uniform.
31    pub ty: UniformType,
32
33    /// If it is an array, the number of elements.
34    pub size: Option<usize>,
35}
36
37/// Information about a uniform block (except its name).
38#[derive(Debug, Clone)]
39pub struct UniformBlock {
40    /// Identifier of the block.
41    ///
42    /// This is internal information, you probably don't need to use it.
43    pub id: i32,
44
45    /// Initial bind point of the block.
46    ///
47    /// This is internal information, you probably don't need to use it.
48    pub initial_binding: i32,
49
50    /// Size in bytes of the data in the block.
51    pub size: usize,
52
53    /// Layout of the block.
54    pub layout: BlockLayout,
55}
56
57/// Layout of a shader storage buffer or a uniform buffer.
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub enum BlockLayout {
60    /// Multiple elements, each having a name.
61    Struct {
62        /// The list of elements, with `name`/`layout` pairs.
63        members: Vec<(String, BlockLayout)>,
64    },
65
66    /// A basic element.
67    BasicType {
68        /// Type of data.
69        ty: UniformType,
70
71        /// Offset of this element in bytes from the start of the buffer.
72        offset_in_buffer: usize,
73    },
74
75    /// A fixed-size array.
76    ///
77    /// For example:
78    ///
79    /// ```notrust
80    /// uint data[12];
81    /// ```
82    Array {
83        /// Type of data of each element.
84        content: Box<BlockLayout>,
85
86        /// Number of elements in the array.
87        length: usize,
88    },
89
90    /// An array whose size isn't known at compile-time. Can only be used as the last element of
91    /// a buffer.
92    ///
93    /// Its actual size depends on the size of the buffer.
94    ///
95    /// For example:
96    ///
97    /// ```notrust
98    /// buffer MyBuffer {
99    ///     uint data[];
100    /// }
101    /// ```
102    DynamicSizedArray {
103        /// Type of data of each element.
104        content: Box<BlockLayout>,
105    },
106}
107
108/// Information about an attribute of a program (except its name).
109///
110/// Internal struct. Not public.
111#[derive(Debug, Copy, Clone)]
112pub struct Attribute {
113    /// The index of the uniform.
114    ///
115    /// This is internal information, you probably don't need to use it.
116    pub location: i32,
117
118    /// Type of the attribute.
119    pub ty: AttributeType,
120
121    /// Number of elements of the attribute.
122    pub size: usize,
123}
124
125/// Describes the layout of a buffer that can receive transform feedback output.
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct TransformFeedbackBuffer {
128    /// Slot of this buffer.
129    ///
130    /// This is internal information, you probably don't need to use it.
131    pub id: i32,
132
133    /// List of elements inside the buffer.
134    pub elements: Vec<TransformFeedbackVarying>,
135
136    /// Size in bytes between two consecutive elements.
137    pub stride: usize,
138}
139
140/// Describes a varying that is being output with transform feedback.
141#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct TransformFeedbackVarying {
143    /// Name of the variable.
144    pub name: String,
145
146    /// Number of bytes between the start of the first element and the start of this one.
147    pub offset: usize,
148
149    /// Size in bytes of this value.
150    pub size: usize,
151
152    /// Type of the value.
153    pub ty: AttributeType,
154}
155
156/// Type of transform feedback. Only used with the legacy interface.
157#[derive(Debug, Copy, Clone, PartialEq, Eq)]
158pub enum TransformFeedbackMode {
159    /// Each value is interleaved in the same buffer.
160    Interleaved,
161
162    /// Each value will go in a separate buffer.
163    Separate,
164}
165
166/// Type of primitives that is being output by transform feedback.
167#[derive(Debug, Copy, Clone, PartialEq, Eq)]
168pub enum OutputPrimitives {
169    /// Points.
170    Points,
171    /// Lines.
172    Lines,
173    /// Triangles.
174    Triangles,
175    /// Quads.
176    Quads,
177}
178
179/// Returns a list of uniforms and a list of atomic counters of a program.
180pub unsafe fn reflect_uniforms(ctxt: &mut CommandContext<'_>, program: Handle)
181                               -> (HashMap<String, Uniform, BuildHasherDefault<FnvHasher>>, HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>>)
182{
183    // number of active uniforms
184    let active_uniforms = {
185        let mut active_uniforms: gl::types::GLint = 0;
186        match program {
187            Handle::Id(program) => {
188                assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
189                        ctxt.version >= &Version(Api::GlEs, 2, 0));
190                ctxt.gl.GetProgramiv(program, gl::ACTIVE_UNIFORMS, &mut active_uniforms);
191            },
192            Handle::Handle(program) => {
193                assert!(ctxt.extensions.gl_arb_shader_objects);
194                ctxt.gl.GetObjectParameterivARB(program, gl::OBJECT_ACTIVE_UNIFORMS_ARB,
195                                                &mut active_uniforms);
196            }
197        };
198        active_uniforms
199    };
200
201    let query_atomic_counters = ctxt.version >= &Version(Api::Gl, 4, 2) || ctxt.version >= &Version(Api::GlEs, 3, 1) ||
202         (ctxt.extensions.gl_arb_program_interface_query && ctxt.extensions.gl_arb_shader_atomic_counters);
203    let mut active_atomic_counters: gl::types::GLint = 0;
204    if query_atomic_counters {
205        let program = if let Handle::Id(program) = program {
206            ctxt.gl.GetProgramiv(program, gl::ACTIVE_ATOMIC_COUNTER_BUFFERS, &mut active_atomic_counters);
207        };
208    }
209
210    // the result of this function
211    let mut uniforms = HashMap::with_hasher(BuildHasherDefault::<FnvHasher>::default());
212    uniforms.reserve((active_uniforms - active_atomic_counters) as usize);
213
214    let mut atomic_counters = HashMap::with_hasher(Default::default());
215    atomic_counters.reserve(active_atomic_counters as usize);
216
217    for uniform_id in 0 .. active_uniforms {
218        let mut uniform_name_tmp: Vec<u8> = Vec::with_capacity(64);
219        let mut uniform_name_tmp_len = 63;
220
221        let mut data_type: gl::types::GLenum = 0;
222        let mut data_size: gl::types::GLint = 0;
223
224        match program {
225            Handle::Id(program) => {
226                assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
227                        ctxt.version >= &Version(Api::GlEs, 2, 0));
228                ctxt.gl.GetActiveUniform(program, uniform_id as gl::types::GLuint,
229                                         uniform_name_tmp_len, &mut uniform_name_tmp_len,
230                                         &mut data_size, &mut data_type,
231                                         uniform_name_tmp.as_mut_ptr() as *mut gl::types::GLchar);
232            },
233            Handle::Handle(program) => {
234                assert!(ctxt.extensions.gl_arb_shader_objects);
235                ctxt.gl.GetActiveUniformARB(program, uniform_id as gl::types::GLuint,
236                                            uniform_name_tmp_len, &mut uniform_name_tmp_len,
237                                            &mut data_size, &mut data_type,
238                                            uniform_name_tmp.as_mut_ptr()
239                                              as *mut gl::types::GLchar);
240            }
241        };
242        uniform_name_tmp.set_len(uniform_name_tmp_len as usize);
243
244        let uniform_name = String::from_utf8(uniform_name_tmp).unwrap();
245        let location = match program {
246            Handle::Id(program) => {
247                assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
248                        ctxt.version >= &Version(Api::GlEs, 2, 0));
249                ctxt.gl.GetUniformLocation(program,
250                                           ffi::CString::new(uniform_name.as_bytes()).unwrap()
251                                             .as_bytes_with_nul().as_ptr() as *const raw::c_char)
252            },
253            Handle::Handle(program) => {
254                assert!(ctxt.extensions.gl_arb_shader_objects);
255                ctxt.gl.GetUniformLocationARB(program,
256                                              ffi::CString::new(uniform_name.as_bytes()).unwrap()
257                                                .as_bytes_with_nul().as_ptr() as *const raw::c_char)
258            }
259        };
260
261
262        if data_type == gl::UNSIGNED_INT_ATOMIC_COUNTER {
263            assert!(query_atomic_counters);
264            let mut atomic_counter_id: gl::types::GLint = 0;
265            let mut atomic_counter_buffer_bind_point: gl::types::GLint = 0;
266            match program {
267                Handle::Id(program) => {
268                    ctxt.gl.GetActiveUniformsiv(program, 1, &(uniform_id as gl::types::GLuint),
269                                                gl::UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX,
270                                                &mut atomic_counter_id);
271                    ctxt.gl.GetActiveAtomicCounterBufferiv(program,
272                                                           atomic_counter_id as gl::types::GLuint,
273                                                           gl::ATOMIC_COUNTER_BUFFER_BINDING,
274                                                           &mut atomic_counter_buffer_bind_point);
275                },
276                Handle::Handle(_) => unreachable!(),
277            }
278            atomic_counters.insert(uniform_name, UniformBlock {
279                id: atomic_counter_id,
280                initial_binding: atomic_counter_buffer_bind_point,
281                size: 4,
282                layout: BlockLayout::BasicType {
283                    ty: UniformType::UnsignedInt,
284                    offset_in_buffer: 0,
285                },
286            });
287        } else {
288            uniforms.insert(uniform_name, Uniform {
289                location: location as i32,
290                ty: glenum_to_uniform_type(data_type),
291                size: if data_size == 1 { None } else { Some(data_size as usize) },
292            });
293        }
294    }
295
296    // Flatten arrays
297    let mut uniforms_flattened = HashMap::with_hasher(Default::default());
298    for uniform in uniforms {
299        // If this is a normal non-array element, just move it over
300        if !uniform.0.ends_with("[0]") {
301            assert!(uniform.1.size.is_none());
302            uniforms_flattened.insert(uniform.0, uniform.1);
303            continue;
304        }
305
306        // We've got an array, first get the base of the name
307        let name_base = uniform.0.split('[').next().unwrap();
308        let uniform_base = uniform.1;
309
310        // Go over all the elements in the array
311        for i in 0..uniform_base.size.unwrap() {
312            let uniform = Uniform {
313                size: None,
314                location: uniform_base.location + (i as i32),
315                .. uniform_base
316            };
317            uniforms_flattened.insert(format!("{}[{}]", name_base, i), uniform);
318        }
319    }
320
321    (uniforms_flattened, atomic_counters)
322}
323
324pub unsafe fn reflect_attributes(ctxt: &mut CommandContext<'_>, program: Handle)
325                                 -> HashMap<String, Attribute, BuildHasherDefault<FnvHasher>>
326{
327    // number of active attributes, and the max length of the attribute names
328    let (active_attributes, attr_name_len_max) = {
329        let mut active_attributes: gl::types::GLint = 0;
330        let mut attr_name_len_max: gl::types::GLint = 0;
331        match program {
332            Handle::Id(program) => {
333                assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
334                        ctxt.version >= &Version(Api::GlEs, 2, 0));
335                ctxt.gl.GetProgramiv(program, gl::ACTIVE_ATTRIBUTES, &mut active_attributes);
336                ctxt.gl.GetProgramiv(program, gl::ACTIVE_ATTRIBUTE_MAX_LENGTH, &mut attr_name_len_max);
337            },
338            Handle::Handle(program) => {
339                assert!(ctxt.extensions.gl_arb_vertex_shader);
340                ctxt.gl.GetObjectParameterivARB(program, gl::OBJECT_ACTIVE_ATTRIBUTES_ARB,
341                                                &mut active_attributes);
342                ctxt.gl.GetObjectParameterivARB(program, gl::OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB,
343                                                &mut attr_name_len_max);
344            }
345        };
346        // let's not trust the driver too much, and clamp the max_len to [63, 2047]
347        attr_name_len_max = cmp::min(cmp::max(attr_name_len_max, 63), 2047);
348
349        (active_attributes, attr_name_len_max)
350    };
351
352    // the result of this function
353    let mut attributes = HashMap::with_hasher(Default::default());
354    attributes.reserve(active_attributes as usize);
355
356    for attribute_id in 0 .. active_attributes {
357        let mut attr_name_tmp: Vec<u8> = Vec::with_capacity((attr_name_len_max + 1) as usize); //+1 for the nul-byte
358        let mut attr_name_len = 0;
359
360        let mut data_type: gl::types::GLenum = 0;
361        let mut data_size: gl::types::GLint = 0;
362
363        match program {
364            Handle::Id(program) => {
365                assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
366                        ctxt.version >= &Version(Api::GlEs, 2, 0));
367                ctxt.gl.GetActiveAttrib(program,
368                                        attribute_id as gl::types::GLuint,
369                                        attr_name_len_max,
370                                        &mut attr_name_len,
371                                        &mut data_size,
372                                        &mut data_type,
373                                        attr_name_tmp.as_mut_ptr() as *mut gl::types::GLchar);
374            },
375            Handle::Handle(program) => {
376                assert!(ctxt.extensions.gl_arb_vertex_shader);
377                ctxt.gl.GetActiveAttribARB(program,
378                                           attribute_id as gl::types::GLuint,
379                                           attr_name_len_max,
380                                           &mut attr_name_len,
381                                           &mut data_size,
382                                           &mut data_type,
383                                           attr_name_tmp.as_mut_ptr() as *mut gl::types::GLchar);
384            }
385        };
386
387        attr_name_tmp.set_len(attr_name_len as usize);
388
389        let attr_name = String::from_utf8(attr_name_tmp).unwrap();
390        if attr_name.starts_with("gl_") {   // ignoring everything built-in
391            continue;
392        }
393
394        if attr_name.is_empty() {
395            // Some spirv compilers add an empty attribute to shaders. Most drivers
396            // don't expose this attribute, but some do.
397            // Since we can't do anything with empty attribute names, we simply skip
398            // them in this reflection code.
399            continue;
400        }
401
402        let location = match program {
403            Handle::Id(program) => {
404                assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
405                        ctxt.version >= &Version(Api::GlEs, 2, 0));
406                ctxt.gl.GetAttribLocation(program,
407                                          ffi::CString::new(attr_name.as_bytes()).unwrap()
408                                            .as_bytes_with_nul().as_ptr() as *const raw::c_char)
409            },
410            Handle::Handle(program) => {
411                assert!(ctxt.extensions.gl_arb_vertex_shader);
412                ctxt.gl.GetAttribLocationARB(program,
413                                             ffi::CString::new(attr_name.as_bytes()).unwrap()
414                                               .as_bytes_with_nul().as_ptr() as *const raw::c_char)
415            }
416        };
417
418        attributes.insert(attr_name, Attribute {
419            location,
420            ty: glenum_to_attribute_type(data_type),
421            size: data_size as usize,
422        });
423    }
424
425    attributes
426}
427
428pub unsafe fn reflect_uniform_blocks(ctxt: &mut CommandContext<'_>, program: Handle)
429                                     -> HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>>
430{
431    // uniform blocks are not supported, so there's none
432    if !(ctxt.version >= &Version(Api::Gl, 3, 1) || ctxt.version >= &Version(Api::GlEs, 3, 0)) {
433        return HashMap::with_hasher(Default::default());
434    }
435
436    let program = match program {
437        Handle::Id(id) => id,
438        _ => unreachable!()
439    };
440
441    let mut active_blocks: gl::types::GLint = 0;
442    ctxt.gl.GetProgramiv(program, gl::ACTIVE_UNIFORM_BLOCKS, &mut active_blocks);
443
444    // WORK-AROUND: AMD OpenGL ES drivers don't accept `GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH`
445    //              even though they report OpenGL ES 3.1. So we return early on if possible.
446    // TODO: find a better work-around ^
447    if active_blocks == 0 {
448        return HashMap::with_hasher(Default::default());
449    }
450
451    let mut active_blocks_max_name_len: gl::types::GLint = 0;
452    ctxt.gl.GetProgramiv(program, gl::ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
453                         &mut active_blocks_max_name_len);
454
455    let mut blocks = HashMap::with_hasher(Default::default());
456    blocks.reserve(active_blocks as usize);
457
458    for block_id in 0 .. active_blocks {
459        // getting the name of the block
460        let name = {
461            let mut name_tmp: Vec<u8> = Vec::with_capacity(1 + active_blocks_max_name_len
462                                                           as usize);
463            let mut name_tmp_len = active_blocks_max_name_len;
464
465            ctxt.gl.GetActiveUniformBlockName(program, block_id as gl::types::GLuint,
466                                              name_tmp_len, &mut name_tmp_len,
467                                              name_tmp.as_mut_ptr() as *mut gl::types::GLchar);
468            name_tmp.set_len(name_tmp_len as usize);
469            String::from_utf8(name_tmp).unwrap()
470        };
471
472        // binding point for this block
473        let mut binding: gl::types::GLint = 0;
474        ctxt.gl.GetActiveUniformBlockiv(program, block_id as gl::types::GLuint,
475                                        gl::UNIFORM_BLOCK_BINDING, &mut binding);
476
477        // number of bytes
478        let mut block_size: gl::types::GLint = 0;
479        ctxt.gl.GetActiveUniformBlockiv(program, block_id as gl::types::GLuint,
480                                        gl::UNIFORM_BLOCK_DATA_SIZE, &mut block_size);
481
482        // number of members
483        let mut num_members: gl::types::GLint = 0;
484        ctxt.gl.GetActiveUniformBlockiv(program, block_id as gl::types::GLuint,
485                                        gl::UNIFORM_BLOCK_ACTIVE_UNIFORMS, &mut num_members);
486
487        // indices of the members
488        let mut members_indices = ::std::iter::repeat(0).take(num_members as usize)
489                                                        .collect::<Vec<gl::types::GLuint>>();
490        ctxt.gl.GetActiveUniformBlockiv(program, block_id as gl::types::GLuint,
491                                        gl::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
492                                        members_indices.as_mut_ptr() as *mut gl::types::GLint);
493
494        // getting the offsets of the members
495        let mut member_offsets = ::std::iter::repeat(0).take(num_members as usize)
496                                                       .collect::<Vec<gl::types::GLint>>();
497        ctxt.gl.GetActiveUniformsiv(program, num_members, members_indices.as_ptr(),
498                                    gl::UNIFORM_OFFSET, member_offsets.as_mut_ptr());
499
500        // getting the types of the members
501        let mut member_types = ::std::iter::repeat(0).take(num_members as usize)
502                                                     .collect::<Vec<gl::types::GLint>>();
503        ctxt.gl.GetActiveUniformsiv(program, num_members, members_indices.as_ptr(),
504                                    gl::UNIFORM_TYPE, member_types.as_mut_ptr());
505
506        // getting the array sizes of the members
507        let mut member_size = ::std::iter::repeat(0).take(num_members as usize)
508                                                    .collect::<Vec<gl::types::GLint>>();
509        ctxt.gl.GetActiveUniformsiv(program, num_members, members_indices.as_ptr(),
510                                    gl::UNIFORM_SIZE, member_size.as_mut_ptr());
511
512        // getting the length of the names of the members
513        let mut member_name_len = ::std::iter::repeat(0).take(num_members as usize)
514                                                         .collect::<Vec<gl::types::GLint>>();
515        ctxt.gl.GetActiveUniformsiv(program, num_members, members_indices.as_ptr(),
516                                    gl::UNIFORM_NAME_LENGTH, member_name_len.as_mut_ptr());
517
518        // getting the names of the members
519        let member_names = member_name_len.iter().zip(members_indices.iter())
520                                          .map(|(&name_len, &index)|
521        {
522            let mut name_tmp: Vec<u8> = Vec::with_capacity(1 + name_len as usize);
523            let mut name_len_tmp = name_len;
524            ctxt.gl.GetActiveUniformName(program, index, name_len, &mut name_len_tmp,
525                                         name_tmp.as_mut_ptr() as *mut gl::types::GLchar);
526            name_tmp.set_len(name_len_tmp as usize);
527
528            String::from_utf8(name_tmp).unwrap()
529        });
530
531        // now computing the list of members
532        let members = member_names.enumerate().map(|(index, name)| {
533            (name, member_offsets[index] as usize,
534             glenum_to_uniform_type(member_types[index] as gl::types::GLenum),
535             member_size[index] as usize, None)
536        });
537
538        // finally inserting into the blocks list
539        blocks.insert(name, UniformBlock {
540            id: block_id as i32,
541            initial_binding: binding as i32,
542            size: block_size as usize,
543            layout: introspection_output_to_layout(members),
544        });
545    }
546
547    blocks
548}
549
550pub unsafe fn reflect_transform_feedback(ctxt: &mut CommandContext<'_>, program: Handle)
551                                         -> Vec<TransformFeedbackBuffer>
552{
553    let program = match program {
554        // transform feedback not supported
555        Handle::Handle(_) => return Vec::with_capacity(0),
556        Handle::Id(id) => id
557    };
558
559    // transform feedback not supported
560    if !(ctxt.version >= &Version(Api::Gl, 3, 0)) && !ctxt.extensions.gl_ext_transform_feedback {
561        return Vec::with_capacity(0);
562    }
563
564    // querying the number of varying
565    let num_varyings = {
566        let mut num_varyings: gl::types::GLint = 0;
567
568        if ctxt.version >= &Version(Api::Gl, 3, 0) {
569            ctxt.gl.GetProgramiv(program, gl::TRANSFORM_FEEDBACK_VARYINGS, &mut num_varyings);
570        } else if ctxt.extensions.gl_ext_transform_feedback {
571            ctxt.gl.GetProgramiv(program, gl::TRANSFORM_FEEDBACK_VARYINGS_EXT, &mut num_varyings);
572        } else {
573            unreachable!();
574        }
575
576        num_varyings
577    };
578
579    // no need to request other things if there are no varying
580    if num_varyings == 0 {
581        return Vec::with_capacity(0);
582    }
583
584    // querying "interleaved" or "separate"
585    let buffer_mode = {
586        let mut buffer_mode: gl::types::GLint = 0;
587
588        if ctxt.version >= &Version(Api::Gl, 3, 0) {
589            ctxt.gl.GetProgramiv(program, gl::TRANSFORM_FEEDBACK_BUFFER_MODE, &mut buffer_mode);
590        } else if ctxt.extensions.gl_ext_transform_feedback {
591            ctxt.gl.GetProgramiv(program, gl::TRANSFORM_FEEDBACK_BUFFER_MODE_EXT, &mut buffer_mode);
592        } else {
593            unreachable!();
594        }
595
596        glenum_to_transform_feedback_mode(buffer_mode as gl::types::GLenum)
597    };
598
599    // the max length includes the null terminator
600    let mut max_buffer_len: gl::types::GLint = 0;
601    if ctxt.version >= &Version(Api::Gl, 3, 0) {
602        ctxt.gl.GetProgramiv(program, gl::TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
603                             &mut max_buffer_len);
604    } else if ctxt.extensions.gl_ext_transform_feedback {
605        ctxt.gl.GetProgramiv(program, gl::TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT,
606                             &mut max_buffer_len);
607    } else {
608        unreachable!();
609    }
610
611    let mut result = Vec::with_capacity(num_varyings as usize);
612
613    for index in 0 .. num_varyings as gl::types::GLuint {
614        let mut name_tmp: Vec<u8> = Vec::with_capacity(max_buffer_len as usize);
615        let mut name_tmp_len = max_buffer_len;
616
617        let mut size = 0;
618        let mut ty = 0;
619
620        if ctxt.version >= &Version(Api::Gl, 3, 0) {
621            ctxt.gl.GetTransformFeedbackVarying(program, index, name_tmp_len, &mut name_tmp_len,
622                                                &mut size, &mut ty, name_tmp.as_mut_ptr()
623                                                as *mut gl::types::GLchar);
624        } else if ctxt.extensions.gl_ext_transform_feedback {
625            ctxt.gl.GetTransformFeedbackVaryingEXT(program, index, name_tmp_len,
626                                                   &mut name_tmp_len, &mut size, &mut ty,
627                                                   name_tmp.as_mut_ptr()
628                                                   as *mut gl::types::GLchar);
629        } else {
630            unreachable!();
631        }
632
633        name_tmp.set_len(name_tmp_len as usize);
634        let name = String::from_utf8(name_tmp).unwrap();
635
636        if buffer_mode == TransformFeedbackMode::Interleaved {
637            if result.len() == 0 {
638                result.push(TransformFeedbackBuffer {
639                    id: 0,
640                    elements: vec![],
641                    stride: 0,
642                });
643            }
644
645            let ty = glenum_to_attribute_type(ty as gl::types::GLenum);
646
647            let prev_size = result[0].stride;
648            result[0].stride += size as usize * ty.get_size_bytes();
649            result[0].elements.push(TransformFeedbackVarying {        // TODO: handle arrays
650                name,
651                size: size as usize * ty.get_size_bytes(),
652                offset: prev_size,
653                ty,
654            });
655
656        } else if buffer_mode == TransformFeedbackMode::Separate {
657            let id = result.len();
658            let ty = glenum_to_attribute_type(ty as gl::types::GLenum);
659            result.push(TransformFeedbackBuffer {
660                id: id as i32,
661                elements: vec![
662                    TransformFeedbackVarying {
663                        name,
664                        size: size as usize * ty.get_size_bytes(),
665                        offset: 0,
666                        ty,
667                    }
668                ],
669                stride: size as usize * ty.get_size_bytes(),
670            });
671
672        } else {
673            unreachable!();
674        }
675    }
676
677    result
678}
679
680/// Obtains the type of data that the geometry shader stage outputs.
681///
682/// # Unsafety
683///
684/// - `program` must be a valid handle to a program.
685/// - The program **must** contain a geometry shader.
686pub unsafe fn reflect_geometry_output_type(ctxt: &mut CommandContext<'_>, program: Handle)
687                                           -> OutputPrimitives
688{
689    let mut value = 0;
690
691    match program {
692        Handle::Id(program) => {
693            assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
694                    ctxt.version >= &Version(Api::GlEs, 2, 0));
695            ctxt.gl.GetProgramiv(program, gl::GEOMETRY_OUTPUT_TYPE, &mut value);
696        },
697        Handle::Handle(program) => {
698            assert!(ctxt.extensions.gl_arb_vertex_shader);
699            ctxt.gl.GetObjectParameterivARB(program, gl::GEOMETRY_OUTPUT_TYPE, &mut value);
700        }
701    };
702
703    match value as gl::types::GLenum {
704        gl::POINTS => OutputPrimitives::Points,
705        gl::LINE_STRIP => OutputPrimitives::Lines,
706        gl::TRIANGLE_STRIP => OutputPrimitives::Triangles,
707        _ => unreachable!()
708    }
709}
710
711/// Obtains the type of data that the tessellation evaluation shader stage outputs.
712///
713/// # Unsafety
714///
715/// - `program` must be a valid handle to a program.
716/// - The program **must** contain a tessellation evaluation shader.
717pub unsafe fn reflect_tess_eval_output_type(ctxt: &mut CommandContext<'_>, program: Handle)
718                                            -> OutputPrimitives
719{
720    let mut value = 0;
721
722    match program {
723        Handle::Id(program) => {
724            assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
725                    ctxt.version >= &Version(Api::GlEs, 2, 0));
726            ctxt.gl.GetProgramiv(program, gl::TESS_GEN_MODE, &mut value);
727        },
728        Handle::Handle(program) => {
729            assert!(ctxt.extensions.gl_arb_vertex_shader);
730            ctxt.gl.GetObjectParameterivARB(program, gl::TESS_GEN_MODE, &mut value);
731        }
732    };
733
734    match value as gl::types::GLenum {
735        gl::TRIANGLES => OutputPrimitives::Triangles,
736        gl::ISOLINES => OutputPrimitives::Lines,
737        gl::QUADS => OutputPrimitives::Quads,
738        _ => unreachable!()
739    }
740}
741
742/// Returns the list of shader storage blocks of a program.
743pub unsafe fn reflect_shader_storage_blocks(ctxt: &mut CommandContext<'_>, program: Handle)
744    -> HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>>
745{
746    if !(ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.version >= &Version(Api::GlEs, 3, 1) ||
747         (ctxt.extensions.gl_arb_program_interface_query && ctxt.extensions.gl_arb_shader_storage_buffer_object))
748    {
749        // not supported
750        return HashMap::with_hasher(Default::default());
751    }
752
753    let program = match program {
754        Handle::Id(program) => program,
755        Handle::Handle(program) => return HashMap::with_hasher(Default::default())
756    };
757
758    // number of active SSBOs
759    let active_blocks = {
760        let mut active_blocks: gl::types::GLint = 0;
761        ctxt.gl.GetProgramInterfaceiv(program, gl::SHADER_STORAGE_BLOCK,
762                                      gl::ACTIVE_RESOURCES, &mut active_blocks);
763        active_blocks as gl::types::GLuint
764    };
765
766    // the result of this function
767    let mut blocks = HashMap::with_hasher(Default::default());
768    blocks.reserve(active_blocks as usize);
769
770    for block_id in 0 .. active_blocks {
771        // getting basic infos
772        let (name_len, num_variables, binding, total_size) = {
773            let mut output: [gl::types::GLint; 4] = [0; 4];
774            ctxt.gl.GetProgramResourceiv(program, gl::SHADER_STORAGE_BLOCK, block_id, 4,
775                                         [gl::NAME_LENGTH, gl::NUM_ACTIVE_VARIABLES,
776                                          gl::BUFFER_BINDING, gl::BUFFER_DATA_SIZE].as_ptr(), 4,
777                                         ptr::null_mut(), output.as_mut_ptr() as *mut _);
778            (output[0] as usize, output[1] as usize, output[2], output[3] as usize)
779        };
780
781        // getting the name of the block
782        let name = {
783            let mut name_tmp: Vec<u8> = Vec::with_capacity(1 + name_len);
784            let mut name_tmp_len = name_len as gl::types::GLsizei;
785
786            ctxt.gl.GetProgramResourceName(program, gl::SHADER_STORAGE_BLOCK, block_id,
787                                           name_tmp_len, &mut name_tmp_len,
788                                           name_tmp.as_mut_ptr() as *mut _);
789            name_tmp.set_len(name_tmp_len as usize);
790            String::from_utf8(name_tmp).unwrap()
791        };
792
793        // indices of the active variables
794        let active_variables: Vec<gl::types::GLint> = {
795            let mut variables = Vec::with_capacity(num_variables);
796            ctxt.gl.GetProgramResourceiv(program, gl::SHADER_STORAGE_BLOCK, block_id, 1,
797                                         [gl::ACTIVE_VARIABLES].as_ptr(),
798                                         num_variables as gl::types::GLsizei,
799                                         ptr::null_mut(), variables.as_mut_ptr() as *mut _);
800            variables.set_len(num_variables);
801            variables
802        };
803
804        // iterator over variables
805        let members = active_variables.into_iter().map(|variable| {
806            let (ty, array_size, offset, _array_stride, name_len, top_level_array_size) = {
807                let mut output: [gl::types::GLint; 6] = [0; 6];
808                ctxt.gl.GetProgramResourceiv(program, gl::BUFFER_VARIABLE,
809                                             variable as gl::types::GLuint, 6,
810                                             [gl::TYPE, gl::ARRAY_SIZE, gl::OFFSET,
811                                              gl::ARRAY_STRIDE, gl::NAME_LENGTH,
812                                              gl::TOP_LEVEL_ARRAY_SIZE].as_ptr(), 6,
813                                             ptr::null_mut(), output.as_mut_ptr() as *mut _);
814                (glenum_to_uniform_type(output[0] as gl::types::GLenum), output[1] as usize,
815                 output[2] as usize, output[3] as usize, output[4] as usize, output[5] as usize)
816            };
817
818            let name = {
819                let mut name_tmp: Vec<u8> = Vec::with_capacity(1 + name_len);
820                let mut name_tmp_len = name_len as gl::types::GLsizei;
821
822                ctxt.gl.GetProgramResourceName(program, gl::BUFFER_VARIABLE,
823                                               variable as gl::types::GLuint,
824                                               name_tmp_len, &mut name_tmp_len,
825                                               name_tmp.as_mut_ptr() as *mut _);
826                name_tmp.set_len(name_tmp_len as usize);
827                String::from_utf8(name_tmp).unwrap()
828            };
829
830            (name, offset, ty, array_size, Some(top_level_array_size))
831        });
832
833        // finally inserting into the blocks list
834        blocks.insert(name, UniformBlock {
835            id: block_id as i32,
836            initial_binding: binding as i32,
837            size: total_size,
838            layout: introspection_output_to_layout(members),
839        });
840    }
841
842    blocks
843}
844
845/// Takes a list of elements produced by OpenGL's introspection API and turns them into
846/// a `BlockLayout` object.
847///
848/// The iterator must produce a list of `(name, offset, ty, array_size, top_level_array_size)`.
849/// The `top_level_array_size` can be `None` if unknown.
850///
851/// # Panic
852///
853/// Panic if the input doesn't conform to the OpenGL specs.
854///
855fn introspection_output_to_layout<I>(elements: I) -> BlockLayout
856                                     where I: Iterator<Item = (String, usize, UniformType,
857                                                               usize, Option<usize>)>
858{
859    // `output` must be a BlockLayout::Struct, otherwise this function will panic
860    fn process(output: &mut BlockLayout, name: &str, offset: usize, ty: UniformType,
861               array_size: usize, top_level_array_size: Option<usize>)
862    {
863        let mut components = name.splitn(2, '.');
864        let current_component = components.next().unwrap();
865        let name_rest = components.next();
866
867        // finding the appropriate place in `output` to write the element
868        let member = if let BlockLayout::Struct { ref mut members } = output {
869            // splitting the name and array size
870            let (current_component, array) = if current_component.ends_with(']') {
871                let open_bracket_pos = current_component.rfind('[').unwrap();
872                let array = current_component[open_bracket_pos + 1 .. current_component.len() - 1]
873                                        .parse().unwrap();
874                (&current_component[.. open_bracket_pos], Some(array))
875            } else {
876                (current_component, None)
877            };
878
879            // because of a bug in Rust's borrow checker, we have to loop twice instead of just
880            // call `if let Some() { } else { }`
881            let existing = members.iter_mut().any(|m| m.0 == current_component);
882            if existing {
883                let member = &mut members.iter_mut().find(|m| m.0 == current_component)
884                                         .unwrap().1;
885
886                if let Some(array) = array {
887                    match member {
888                        BlockLayout::Array { ref mut content, ref mut length } => {
889                            if *length <= array { *length = array + 1; }
890                            &mut **content
891                        },
892                        BlockLayout::DynamicSizedArray { ref mut content } => {
893                            &mut **content
894                        },
895                        _ => unreachable!()
896                    }
897                } else {
898                    member
899                }
900
901            } else {
902                // member doesn't exist yet in the output, adding it
903                if let Some(array) = array {
904                    if top_level_array_size == Some(0) {
905                        members.push((current_component.to_owned(), BlockLayout::DynamicSizedArray {
906                            content: Box::new(BlockLayout::Struct { members: Vec::new() }),
907                        }));
908                    } else {
909                        members.push((current_component.to_owned(), BlockLayout::Array {
910                            content: Box::new(BlockLayout::Struct { members: Vec::new() }),
911                            length: if name_rest.is_some() { array } else { array_size },
912                        }));
913                    }
914
915                    match &mut members.last_mut().unwrap().1 {
916                        BlockLayout::Array { ref mut content, .. } => &mut **content,
917                        BlockLayout::DynamicSizedArray { ref mut content } => &mut **content,
918                        _ => unreachable!()
919                    }
920
921                } else {
922                    members.push((current_component.to_owned(), BlockLayout::Struct {
923                        members: Vec::new()
924                    }));
925                    &mut members.last_mut().unwrap().1
926                }
927            }
928
929        } else {
930            unreachable!();
931        };
932
933        // now adding either the other elements or the final element itself
934        if let Some(name_rest) = name_rest {
935            process(member, name_rest, offset, ty, array_size, None);
936
937        } else {
938            // don't write over the offset in buffer
939            match *member {
940                BlockLayout::BasicType { ty: ty_ex, .. } if ty_ex == ty => (),
941                _ => {
942                    *member = BlockLayout::BasicType {
943                        offset_in_buffer: offset,
944                        ty,
945                    };
946                }
947            }
948        }
949    }
950
951    // ↓ actual body of `introspection_output_to_layout` starts here ↓
952    let mut layout = BlockLayout::Struct { members: Vec::new() };
953    for (name, offset, ty, array_size, top_level_array_size) in elements {
954        process(&mut layout, &name, offset, ty, array_size, top_level_array_size);
955    }
956    layout
957}
958
959#[inline]
960fn glenum_to_uniform_type(ty: gl::types::GLenum) -> UniformType {
961    match ty {
962        gl::FLOAT => UniformType::Float,
963        gl::FLOAT_VEC2 => UniformType::FloatVec2,
964        gl::FLOAT_VEC3 => UniformType::FloatVec3,
965        gl::FLOAT_VEC4 => UniformType::FloatVec4,
966        gl::DOUBLE => UniformType::Double,
967        gl::DOUBLE_VEC2 => UniformType::DoubleVec2,
968        gl::DOUBLE_VEC3 => UniformType::DoubleVec3,
969        gl::DOUBLE_VEC4 => UniformType::DoubleVec4,
970        gl::INT => UniformType::Int,
971        gl::INT_VEC2 => UniformType::IntVec2,
972        gl::INT_VEC3 => UniformType::IntVec3,
973        gl::INT_VEC4 => UniformType::IntVec4,
974        gl::UNSIGNED_INT => UniformType::UnsignedInt,
975        gl::UNSIGNED_INT_VEC2 => UniformType::UnsignedIntVec2,
976        gl::UNSIGNED_INT_VEC3 => UniformType::UnsignedIntVec3,
977        gl::UNSIGNED_INT_VEC4 => UniformType::UnsignedIntVec4,
978        gl::BOOL => UniformType::Bool,
979        gl::BOOL_VEC2 => UniformType::BoolVec2,
980        gl::BOOL_VEC3 => UniformType::BoolVec3,
981        gl::BOOL_VEC4 => UniformType::BoolVec4,
982        gl::FLOAT_MAT2 => UniformType::FloatMat2,
983        gl::FLOAT_MAT3 => UniformType::FloatMat3,
984        gl::FLOAT_MAT4 => UniformType::FloatMat4,
985        gl::FLOAT_MAT2x3 => UniformType::FloatMat2x3,
986        gl::FLOAT_MAT2x4 => UniformType::FloatMat2x4,
987        gl::FLOAT_MAT3x2 => UniformType::FloatMat3x2,
988        gl::FLOAT_MAT3x4 => UniformType::FloatMat3x4,
989        gl::FLOAT_MAT4x2 => UniformType::FloatMat4x2,
990        gl::FLOAT_MAT4x3 => UniformType::FloatMat4x3,
991        gl::DOUBLE_MAT2 => UniformType::DoubleMat2,
992        gl::DOUBLE_MAT3 => UniformType::DoubleMat3,
993        gl::DOUBLE_MAT4 => UniformType::DoubleMat4,
994        gl::DOUBLE_MAT2x3 => UniformType::DoubleMat2x3,
995        gl::DOUBLE_MAT2x4 => UniformType::DoubleMat2x4,
996        gl::DOUBLE_MAT3x2 => UniformType::DoubleMat3x2,
997        gl::DOUBLE_MAT3x4 => UniformType::DoubleMat3x4,
998        gl::DOUBLE_MAT4x2 => UniformType::DoubleMat4x2,
999        gl::DOUBLE_MAT4x3 => UniformType::DoubleMat4x3,
1000        gl::SAMPLER_1D => UniformType::Sampler1d,
1001        gl::SAMPLER_2D => UniformType::Sampler2d,
1002        gl::SAMPLER_3D => UniformType::Sampler3d,
1003        gl::SAMPLER_CUBE => UniformType::SamplerCube,
1004        gl::SAMPLER_1D_SHADOW => UniformType::Sampler1dShadow,
1005        gl::SAMPLER_2D_SHADOW => UniformType::Sampler2dShadow,
1006        gl::SAMPLER_1D_ARRAY => UniformType::Sampler1dArray,
1007        gl::SAMPLER_2D_ARRAY => UniformType::Sampler2dArray,
1008        gl::SAMPLER_CUBE_MAP_ARRAY => UniformType::SamplerCubeArray,
1009        gl::SAMPLER_1D_ARRAY_SHADOW => UniformType::Sampler1dArrayShadow,
1010        gl::SAMPLER_2D_ARRAY_SHADOW => UniformType::Sampler2dArrayShadow,
1011        gl::SAMPLER_2D_MULTISAMPLE => UniformType::Sampler2dMultisample,
1012        gl::SAMPLER_2D_MULTISAMPLE_ARRAY => UniformType::Sampler2dMultisampleArray,
1013        gl::SAMPLER_CUBE_SHADOW => UniformType::SamplerCubeShadow,
1014        gl::SAMPLER_BUFFER => UniformType::SamplerBuffer,
1015        gl::SAMPLER_2D_RECT => UniformType::Sampler2dRect,
1016        gl::SAMPLER_2D_RECT_SHADOW => UniformType::Sampler2dRectShadow,
1017        gl::INT_SAMPLER_1D => UniformType::ISampler1d,
1018        gl::INT_SAMPLER_2D => UniformType::ISampler2d,
1019        gl::INT_SAMPLER_3D => UniformType::ISampler3d,
1020        gl::INT_SAMPLER_CUBE => UniformType::ISamplerCube,
1021        gl::INT_SAMPLER_1D_ARRAY => UniformType::ISampler1dArray,
1022        gl::INT_SAMPLER_2D_ARRAY => UniformType::ISampler2dArray,
1023        gl::INT_SAMPLER_CUBE_MAP_ARRAY => UniformType::ISamplerCubeArray,
1024        gl::INT_SAMPLER_2D_MULTISAMPLE => UniformType::ISampler2dMultisample,
1025        gl::INT_SAMPLER_2D_MULTISAMPLE_ARRAY => UniformType::ISampler2dMultisampleArray,
1026        gl::INT_SAMPLER_BUFFER => UniformType::ISamplerBuffer,
1027        gl::INT_SAMPLER_2D_RECT => UniformType::ISampler2dRect,
1028        gl::UNSIGNED_INT_SAMPLER_1D => UniformType::USampler1d,
1029        gl::UNSIGNED_INT_SAMPLER_2D => UniformType::USampler2d,
1030        gl::UNSIGNED_INT_SAMPLER_3D => UniformType::USampler3d,
1031        gl::UNSIGNED_INT_SAMPLER_CUBE => UniformType::USamplerCube,
1032        gl::UNSIGNED_INT_SAMPLER_1D_ARRAY => UniformType::USampler2dArray,
1033        gl::UNSIGNED_INT_SAMPLER_2D_ARRAY => UniformType::USampler2dArray,
1034        gl::UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY => UniformType::USamplerCubeArray,
1035        gl::UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE => UniformType::USampler2dMultisample,
1036        gl::UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY => UniformType::USampler2dMultisampleArray,
1037        gl::UNSIGNED_INT_SAMPLER_BUFFER => UniformType::USamplerBuffer,
1038        gl::UNSIGNED_INT_SAMPLER_2D_RECT => UniformType::USampler2dRect,
1039        gl::IMAGE_1D => UniformType::Image1d,
1040        gl::IMAGE_2D => UniformType::Image2d,
1041        gl::IMAGE_3D => UniformType::Image3d,
1042        gl::IMAGE_2D_RECT => UniformType::Image2dRect,
1043        gl::IMAGE_CUBE => UniformType::ImageCube,
1044        gl::IMAGE_BUFFER => UniformType::ImageBuffer,
1045        gl::IMAGE_1D_ARRAY => UniformType::Image1dArray,
1046        gl::IMAGE_2D_ARRAY => UniformType::Image2dArray,
1047        gl::IMAGE_2D_MULTISAMPLE => UniformType::Image2dMultisample,
1048        gl::IMAGE_2D_MULTISAMPLE_ARRAY => UniformType::Image2dMultisampleArray,
1049        gl::INT_IMAGE_1D => UniformType::IImage1d,
1050        gl::INT_IMAGE_2D => UniformType::IImage2d,
1051        gl::INT_IMAGE_3D => UniformType::IImage3d,
1052        gl::INT_IMAGE_2D_RECT => UniformType::IImage2dRect,
1053        gl::INT_IMAGE_CUBE => UniformType::IImageCube,
1054        gl::INT_IMAGE_BUFFER => UniformType::IImageBuffer,
1055        gl::INT_IMAGE_1D_ARRAY => UniformType::IImage1dArray,
1056        gl::INT_IMAGE_2D_ARRAY => UniformType::IImage2dArray,
1057        gl::INT_IMAGE_2D_MULTISAMPLE => UniformType::IImage2dMultisample,
1058        gl::INT_IMAGE_2D_MULTISAMPLE_ARRAY => UniformType::IImage2dMultisampleArray,
1059        gl::UNSIGNED_INT_IMAGE_1D => UniformType::UImage1d,
1060        gl::UNSIGNED_INT_IMAGE_2D => UniformType::UImage2d,
1061        gl::UNSIGNED_INT_IMAGE_3D => UniformType::UImage3d,
1062        gl::UNSIGNED_INT_IMAGE_2D_RECT => UniformType::UImage2dRect,
1063        gl::UNSIGNED_INT_IMAGE_CUBE => UniformType::UImageCube,
1064        gl::UNSIGNED_INT_IMAGE_BUFFER => UniformType::UImageBuffer,
1065        gl::UNSIGNED_INT_IMAGE_1D_ARRAY => UniformType::UImage1dArray,
1066        gl::UNSIGNED_INT_IMAGE_2D_ARRAY => UniformType::UImage2dArray,
1067        gl::UNSIGNED_INT_IMAGE_2D_MULTISAMPLE => UniformType::UImage2dMultisample,
1068        gl::UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY => UniformType::UImage2dMultisampleArray,
1069        gl::UNSIGNED_INT_ATOMIC_COUNTER => UniformType::AtomicCounterUint,
1070        v => panic!("Unknown value returned by OpenGL uniform type: {}", v)
1071    }
1072}
1073
1074#[inline]
1075fn glenum_to_attribute_type(value: gl::types::GLenum) -> AttributeType {
1076    match value {
1077        gl::FLOAT => AttributeType::F32,
1078        gl::FLOAT_VEC2 => AttributeType::F32F32,
1079        gl::FLOAT_VEC3 => AttributeType::F32F32F32,
1080        gl::FLOAT_VEC4 => AttributeType::F32F32F32F32,
1081        gl::INT => AttributeType::I32,
1082        gl::INT_VEC2 => AttributeType::I32I32,
1083        gl::INT_VEC3 => AttributeType::I32I32I32,
1084        gl::INT_VEC4 => AttributeType::I32I32I32I32,
1085        gl::UNSIGNED_INT => AttributeType::U32,
1086        gl::UNSIGNED_INT_VEC2 => AttributeType::U32U32,
1087        //gl::UNSIGNED_INT_VEC2_EXT => AttributeType::U32U32,
1088        gl::UNSIGNED_INT_VEC3 => AttributeType::U32U32U32,
1089        //gl::UNSIGNED_INT_VEC3_EXT => AttributeType::U32U32U32,
1090        gl::UNSIGNED_INT_VEC4 => AttributeType::U32U32U32U32,
1091        //gl::UNSIGNED_INT_VEC4_EXT => AttributeType::U32U32U32U32,
1092        gl::FLOAT_MAT2 => AttributeType::F32x2x2,
1093        gl::FLOAT_MAT3 => AttributeType::F32x3x3,
1094        gl::FLOAT_MAT4 => AttributeType::F32x4x4,
1095        gl::FLOAT_MAT2x3 => AttributeType::F32x2x3,
1096        gl::FLOAT_MAT2x4 => AttributeType::F32x2x4,
1097        gl::FLOAT_MAT3x2 => AttributeType::F32x3x2,
1098        gl::FLOAT_MAT3x4 => AttributeType::F32x3x4,
1099        gl::FLOAT_MAT4x2 => AttributeType::F32x4x2,
1100        gl::FLOAT_MAT4x3 => AttributeType::F32x4x3,
1101        gl::DOUBLE => AttributeType::F64,
1102        gl::DOUBLE_VEC2 => AttributeType::F64F64,
1103        gl::DOUBLE_VEC3 => AttributeType::F64F64F64,
1104        gl::DOUBLE_VEC4 => AttributeType::F64F64F64F64,
1105        v => panic!("Unknown value returned by OpenGL attribute type: {}", v)
1106    }
1107}
1108
1109#[inline]
1110fn glenum_to_transform_feedback_mode(value: gl::types::GLenum) -> TransformFeedbackMode {
1111    match value {
1112        gl::INTERLEAVED_ATTRIBS/* | gl::INTERLEAVED_ATTRIBS_EXT*/ => {
1113            TransformFeedbackMode::Interleaved
1114        },
1115        gl::SEPARATE_ATTRIBS/* | gl::SEPARATE_ATTRIBS_EXT*/ => {
1116            TransformFeedbackMode::Separate
1117        },
1118        v => panic!("Unknown value returned by OpenGL varying mode: {}", v)
1119    }
1120}
1121
1122/// Contains all subroutine data of a program.
1123#[derive(Debug, Default, Clone)]
1124pub struct SubroutineData {
1125    /// Number of subroutine uniform locations per shader stage.
1126    /// This is *not* equal to the number of subroutine uniforms per stage,
1127    /// because users can use `#layout(location=...)`.
1128    pub location_counts: HashMap<ShaderStage, usize, BuildHasherDefault<FnvHasher>>,
1129
1130    /// The list of all subroutine uniforms of the program stored in a structured way to enable fast lookups.
1131    /// A subroutine uniform is uniquely defined by a name and a shader stage.
1132    pub subroutine_uniforms: HashMap<(String, ShaderStage), SubroutineUniform,
1133                                     BuildHasherDefault<FnvHasher>>,
1134}
1135
1136/// Information about a Subroutine Uniform (except name)
1137#[derive(Debug, Clone)]
1138pub struct SubroutineUniform {
1139
1140    /// The index of the subroutine uniform.
1141    /// Needed to query information from the OpenGL backend.
1142    pub index: u32,
1143
1144    /// The location of the uniform.
1145    /// This is used to bind subroutines to this subroutine uniform.
1146    pub location: i32,
1147
1148    /// If the uniform is an array, the size of the array.
1149    pub size: Option<usize>,
1150
1151    /// A list of subroutines that can potentially be used with this uniform.
1152    pub compatible_subroutines: Vec<Subroutine>,
1153}
1154
1155/// Information about a subroutine.
1156#[derive(Debug, Clone)]
1157pub struct Subroutine {
1158    /// The index of the subroutine, needed to bind this to a subroutine uniform.
1159    pub index: u32,
1160
1161    /// The name of the subroutine.
1162    pub name: String,
1163}
1164
1165/// The different stages of the program pipeline.
1166#[allow(missing_docs)]
1167#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1168pub enum ShaderStage {
1169    Vertex,
1170    Fragment,
1171    TessellationControl,
1172    TessellationEvaluation,
1173    Geometry,
1174    // FIXME? According to https://www.opengl.org/sdk/docs/man/html/glUniformSubroutines.xhtml ,
1175    // compute shaders are not supported.
1176    // Compute,
1177}
1178
1179impl ShaderStage {
1180    /// Converts the `ShaderStage` to its GLenum equivalent
1181    pub fn to_gl_enum(&self) -> gl::types::GLenum {
1182        match *self {
1183            ShaderStage::Vertex => gl::VERTEX_SHADER,
1184            ShaderStage::Fragment => gl::FRAGMENT_SHADER,
1185            ShaderStage::TessellationControl => gl::TESS_CONTROL_SHADER,
1186            ShaderStage::TessellationEvaluation => gl::TESS_EVALUATION_SHADER,
1187            ShaderStage::Geometry => gl::GEOMETRY_SHADER,
1188            // Compute => gl::COMPUTE_SHADER,
1189        }
1190    }
1191}
1192
1193fn get_shader_stages(has_geometry_shader: bool,
1194                     has_tessellation_control_shader: bool,
1195                     has_tessellation_evaluation_shader: bool)
1196                     -> Vec<ShaderStage> {
1197    let mut stages = vec![ShaderStage::Vertex, ShaderStage::Fragment];
1198    if has_tessellation_evaluation_shader {
1199        stages.push(ShaderStage::TessellationEvaluation);
1200    }
1201    if has_tessellation_control_shader {
1202        stages.push(ShaderStage::TessellationControl);
1203    }
1204    if has_geometry_shader {
1205        stages.push(ShaderStage::Geometry);
1206    }
1207    stages
1208}
1209
1210/// Returns the data associated with a programs subroutines.
1211pub unsafe fn reflect_subroutine_data(ctxt: &mut CommandContext<'_>, program: Handle,
1212                                      has_geometry_shader: bool,
1213                                      has_tessellation_control_shader: bool,
1214                                      has_tessellation_evaluation_shader: bool)
1215                                      -> SubroutineData
1216{
1217    if !program::is_subroutine_supported(ctxt) {
1218        return Default::default();
1219    }
1220
1221    let program = match program {
1222        // subroutines not supported.
1223        Handle::Handle(_) => return Default::default(),
1224        Handle::Id(id) => id
1225    };
1226
1227    // Compute shader programs are not handled: #1718
1228    let mut shader_count = 0;
1229    ctxt.gl.GetProgramiv(program, gl::ATTACHED_SHADERS, &mut shader_count);
1230    if shader_count < 2 {
1231        return Default::default();
1232    }
1233
1234    let shader_stages = get_shader_stages(has_geometry_shader,
1235                                          has_tessellation_control_shader,
1236                                          has_tessellation_evaluation_shader);
1237    let mut subroutine_uniforms = HashMap::with_hasher(Default::default());
1238    let mut location_counts = HashMap::with_hasher(Default::default());
1239    for stage in shader_stages.iter() {
1240        let mut location_count: gl::types::GLint = 0;
1241        ctxt.gl.GetProgramStageiv(program, stage.to_gl_enum(),
1242                                  gl::ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS,
1243                                  &mut location_count);
1244        location_counts.insert(*stage, location_count as usize);
1245        let mut subroutine_count: gl::types::GLint = 0;
1246        ctxt.gl.GetProgramStageiv(program, stage.to_gl_enum(),
1247                                  gl::ACTIVE_SUBROUTINE_UNIFORMS,
1248                                  &mut subroutine_count);
1249        for i in 0..subroutine_count {
1250            // Get the name of the uniform
1251            let mut uniform_name_tmp: Vec<u8> = vec![0; 64];
1252            let mut name_len: gl::types::GLsizei = 0;
1253            ctxt.gl.GetActiveSubroutineUniformName(program, stage.to_gl_enum(),
1254                                                   i as gl::types::GLuint,
1255                                                   (uniform_name_tmp.len() - 1) as gl::types::GLint,
1256                                                   &mut name_len,
1257                                                   uniform_name_tmp.as_mut_ptr() as *mut gl::types::GLchar);
1258
1259            let location = ctxt.gl.GetSubroutineUniformLocation(program, stage.to_gl_enum(),
1260                                                                uniform_name_tmp.as_ptr() as *const gl::types::GLchar);
1261
1262            uniform_name_tmp.set_len(name_len as usize);
1263            let uniform_name = String::from_utf8(uniform_name_tmp).unwrap();
1264
1265            let mut size: gl::types::GLint = 0;
1266            ctxt.gl.GetActiveSubroutineUniformiv(program, stage.to_gl_enum(), i as u32,
1267                                         gl::UNIFORM_SIZE, &mut size);
1268            let size = if size == 1 {
1269                None
1270            } else {
1271                Some(size as usize)
1272            };
1273
1274            // Get the number of compatible subroutines.
1275            let mut compatible_count: gl::types::GLint = 0;
1276            ctxt.gl.GetActiveSubroutineUniformiv(program, stage.to_gl_enum(), i as u32,
1277                                                 gl::NUM_COMPATIBLE_SUBROUTINES, &mut compatible_count);
1278
1279            // Get the indices of compatible subroutines.
1280            let mut compatible_sr_indices: Vec<gl::types::GLuint> = Vec::with_capacity(compatible_count as usize);
1281            ctxt.gl.GetActiveSubroutineUniformiv(program, stage.to_gl_enum(),
1282                                                 i as gl::types::GLuint, gl::COMPATIBLE_SUBROUTINES,
1283                                                 compatible_sr_indices.as_mut_ptr() as *mut gl::types::GLint);
1284            compatible_sr_indices.set_len(compatible_count as usize);
1285            let mut compatible_subroutines: Vec<Subroutine> = Vec::new();
1286            for j in 0..compatible_count {
1287                // Get the names of compatible subroutines.
1288                let mut subroutine_name_tmp: Vec<u8> = vec![0; 64];
1289                let mut name_len: gl::types::GLsizei = 0;
1290                ctxt.gl.GetActiveSubroutineName(program, stage.to_gl_enum(), compatible_sr_indices[j as usize],
1291                                                subroutine_name_tmp.len() as gl::types::GLint,
1292                                                &mut name_len,
1293                                                subroutine_name_tmp.as_mut_ptr() as *mut gl::types::GLchar);
1294
1295                subroutine_name_tmp.set_len(name_len as usize);
1296                let subroutine_name = String::from_utf8(subroutine_name_tmp).unwrap();
1297                compatible_subroutines.push(
1298                    Subroutine {
1299                        index: compatible_sr_indices[j as usize],
1300                        name: subroutine_name,
1301                    }
1302                );
1303            }
1304
1305            let subroutine_uniform = SubroutineUniform {
1306                index: i as u32,
1307                location,
1308                size,
1309                compatible_subroutines,
1310            };
1311            subroutine_uniforms.insert((uniform_name, *stage), subroutine_uniform);
1312        }
1313    }
1314    SubroutineData {
1315        location_counts,
1316        subroutine_uniforms
1317    }
1318}