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#[derive(Debug, Copy, Clone)]
24pub struct Uniform {
25 pub location: i32,
29
30 pub ty: UniformType,
32
33 pub size: Option<usize>,
35}
36
37#[derive(Debug, Clone)]
39pub struct UniformBlock {
40 pub id: i32,
44
45 pub initial_binding: i32,
49
50 pub size: usize,
52
53 pub layout: BlockLayout,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub enum BlockLayout {
60 Struct {
62 members: Vec<(String, BlockLayout)>,
64 },
65
66 BasicType {
68 ty: UniformType,
70
71 offset_in_buffer: usize,
73 },
74
75 Array {
83 content: Box<BlockLayout>,
85
86 length: usize,
88 },
89
90 DynamicSizedArray {
103 content: Box<BlockLayout>,
105 },
106}
107
108#[derive(Debug, Copy, Clone)]
112pub struct Attribute {
113 pub location: i32,
117
118 pub ty: AttributeType,
120
121 pub size: usize,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct TransformFeedbackBuffer {
128 pub id: i32,
132
133 pub elements: Vec<TransformFeedbackVarying>,
135
136 pub stride: usize,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct TransformFeedbackVarying {
143 pub name: String,
145
146 pub offset: usize,
148
149 pub size: usize,
151
152 pub ty: AttributeType,
154}
155
156#[derive(Debug, Copy, Clone, PartialEq, Eq)]
158pub enum TransformFeedbackMode {
159 Interleaved,
161
162 Separate,
164}
165
166#[derive(Debug, Copy, Clone, PartialEq, Eq)]
168pub enum OutputPrimitives {
169 Points,
171 Lines,
173 Triangles,
175 Quads,
177}
178
179pub unsafe fn reflect_uniforms(ctxt: &mut CommandContext<'_>, program: Handle)
181 -> (HashMap<String, Uniform, BuildHasherDefault<FnvHasher>>, HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>>)
182{
183 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 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 let mut uniforms_flattened = HashMap::with_hasher(Default::default());
298 for uniform in uniforms {
299 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 let name_base = uniform.0.split('[').next().unwrap();
308 let uniform_base = uniform.1;
309
310 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 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 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 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); 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_") { continue;
392 }
393
394 if attr_name.is_empty() {
395 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Handle::Handle(_) => return Vec::with_capacity(0),
556 Handle::Id(id) => id
557 };
558
559 if !(ctxt.version >= &Version(Api::Gl, 3, 0)) && !ctxt.extensions.gl_ext_transform_feedback {
561 return Vec::with_capacity(0);
562 }
563
564 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 if num_varyings == 0 {
581 return Vec::with_capacity(0);
582 }
583
584 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 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 { 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
680pub 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
711pub 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
742pub 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 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 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 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 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 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 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 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 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
845fn introspection_output_to_layout<I>(elements: I) -> BlockLayout
856 where I: Iterator<Item = (String, usize, UniformType,
857 usize, Option<usize>)>
858{
859 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 let member = if let BlockLayout::Struct { ref mut members } = output {
869 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 (¤t_component[.. open_bracket_pos], Some(array))
875 } else {
876 (current_component, None)
877 };
878
879 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 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 if let Some(name_rest) = name_rest {
935 process(member, name_rest, offset, ty, array_size, None);
936
937 } else {
938 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 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_VEC3 => AttributeType::U32U32U32,
1089 gl::UNSIGNED_INT_VEC4 => AttributeType::U32U32U32U32,
1091 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=> {
1113 TransformFeedbackMode::Interleaved
1114 },
1115 gl::SEPARATE_ATTRIBS=> {
1116 TransformFeedbackMode::Separate
1117 },
1118 v => panic!("Unknown value returned by OpenGL varying mode: {}", v)
1119 }
1120}
1121
1122#[derive(Debug, Default, Clone)]
1124pub struct SubroutineData {
1125 pub location_counts: HashMap<ShaderStage, usize, BuildHasherDefault<FnvHasher>>,
1129
1130 pub subroutine_uniforms: HashMap<(String, ShaderStage), SubroutineUniform,
1133 BuildHasherDefault<FnvHasher>>,
1134}
1135
1136#[derive(Debug, Clone)]
1138pub struct SubroutineUniform {
1139
1140 pub index: u32,
1143
1144 pub location: i32,
1147
1148 pub size: Option<usize>,
1150
1151 pub compatible_subroutines: Vec<Subroutine>,
1153}
1154
1155#[derive(Debug, Clone)]
1157pub struct Subroutine {
1158 pub index: u32,
1160
1161 pub name: String,
1163}
1164
1165#[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 }
1178
1179impl ShaderStage {
1180 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 }
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
1210pub 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 Handle::Handle(_) => return Default::default(),
1224 Handle::Id(id) => id
1225 };
1226
1227 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 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 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 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 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}