1use crate::context::ExtensionsList;
2use crate::version::Version;
3use crate::version::Api;
4
5use std::cmp;
6use std::collections::HashMap;
7use std::ffi::CStr;
8use std::hash::BuildHasherDefault;
9
10use fnv::FnvHasher;
11
12use crate::gl;
13use crate::ToGlEnum;
14
15use crate::CapabilitiesSource;
16use crate::image_format::TextureFormat;
17
18#[derive(Debug, Copy, Clone)]
20pub enum Profile {
21 Core,
23 Compatibility
25}
26
27#[derive(Debug)]
31pub struct Capabilities {
32 pub supported_glsl_versions: Vec<Version>,
36
37 pub version: String,
40
41 pub vendor: String,
43
44 pub renderer: String,
47
48 pub profile: Option<Profile>,
52
53 pub debug: bool,
56
57 pub forward_compatible: bool,
60
61 pub robustness: bool,
63
64 pub can_lose_context: bool,
66
67 pub release_behavior: ReleaseBehavior,
69
70 pub stereo: bool,
72
73 pub srgb: bool,
75
76 pub depth_bits: Option<u16>,
78
79 pub stencil_bits: Option<u16>,
81
82 pub internal_formats_textures: HashMap<TextureFormat, FormatInfos, BuildHasherDefault<FnvHasher>>,
84
85 pub internal_formats_renderbuffers: HashMap<TextureFormat, FormatInfos, BuildHasherDefault<FnvHasher>>,
87
88 pub max_combined_texture_image_units: gl::types::GLint,
92
93 pub max_texture_max_anisotropy: Option<gl::types::GLfloat>,
97
98 pub max_texture_size: gl::types::GLint,
100
101 pub max_texture_buffer_size: Option<gl::types::GLint>,
103
104 pub max_viewport_dims: (gl::types::GLint, gl::types::GLint),
106
107 pub max_draw_buffers: gl::types::GLint,
109
110 pub max_patch_vertices: Option<gl::types::GLint>,
112
113 pub max_indexed_atomic_counter_buffer: gl::types::GLint,
115
116 pub max_indexed_shader_storage_buffer: gl::types::GLint,
118
119 pub max_indexed_transform_feedback_buffer: gl::types::GLint,
121
122 pub max_indexed_uniform_buffer: gl::types::GLint,
124
125 pub max_compute_work_group_count: (gl::types::GLint, gl::types::GLint, gl::types::GLint),
127
128 pub max_color_attachments: gl::types::GLint,
130
131 pub max_framebuffer_width: Option<gl::types::GLint>,
133
134 pub max_framebuffer_height: Option<gl::types::GLint>,
136
137 pub max_framebuffer_layers: Option<gl::types::GLint>,
139
140 pub max_framebuffer_samples: Option<gl::types::GLint>,
142}
143
144#[derive(Debug)]
146pub struct FormatInfos {
147 pub multisamples: Option<Vec<gl::types::GLint>>,
149}
150
151#[derive(Copy, Clone, Debug, PartialEq, Eq)]
153pub enum ReleaseBehavior {
154 None,
156
157 Flush,
159}
160
161pub unsafe fn get_capabilities(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList)
171 -> Capabilities
172{
173 let (debug, forward_compatible) = if version >= &Version(Api::Gl, 3, 0) {
175 let mut val = 0;
176 gl.GetIntegerv(gl::CONTEXT_FLAGS, &mut val);
177 let val = val as gl::types::GLenum;
178 ((val & gl::CONTEXT_FLAG_DEBUG_BIT) != 0,
179 (val & gl::CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) != 0)
180 } else {
181 (false, false)
182 };
183
184 let renderer = {
186 let s = gl.GetString(gl::RENDERER);
187 assert!(!s.is_null());
188 String::from_utf8(CStr::from_ptr(s as *const _).to_bytes().to_vec()).ok()
189 .expect("glGetString(GL_RENDERER) returned a non-UTF8 string")
190 };
191
192 Capabilities {
193 supported_glsl_versions: {
194 get_supported_glsl(gl, version, extensions)
195 },
196
197 version: {
198 let s = gl.GetString(gl::VERSION);
199 assert!(!s.is_null());
200 String::from_utf8(CStr::from_ptr(s as *const _).to_bytes().to_vec()).ok()
201 .expect("glGetString(GL_VERSION) returned a non-UTF8 string")
202 },
203
204 vendor: {
205 let s = gl.GetString(gl::VENDOR);
206 assert!(!s.is_null());
207 String::from_utf8(CStr::from_ptr(s as *const _).to_bytes().to_vec()).ok()
208 .expect("glGetString(GL_VENDOR) returned a non-UTF8 string")
209 },
210
211 profile: {
212 if version >= &Version(Api::Gl, 3, 2) {
213 let mut val = 0;
214 gl.GetIntegerv(gl::CONTEXT_PROFILE_MASK, &mut val);
215 let val = val as gl::types::GLenum;
216 if (val & gl::CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0 {
217 Some(Profile::Compatibility)
218 } else if (val & gl::CONTEXT_CORE_PROFILE_BIT) != 0 {
219 Some(Profile::Core)
220 } else {
221 None
222 }
223 } else {
224 None
225 }
226 },
227
228 debug,
229
230 forward_compatible,
231
232 robustness: if version >= &Version(Api::Gl, 4, 5) || version >= &Version(Api::GlEs, 3, 2) ||
233 (version >= &Version(Api::Gl, 3, 0) && extensions.gl_arb_robustness)
234 {
235 let mut val = 0;
238 gl.GetIntegerv(gl::CONTEXT_FLAGS, &mut val);
239 let val = val as gl::types::GLenum;
240 (val & gl::CONTEXT_FLAG_ROBUST_ACCESS_BIT) != 0
241
242 } else if extensions.gl_khr_robustness || extensions.gl_ext_robustness {
243 let mut val = 0;
244 gl.GetBooleanv(gl::CONTEXT_ROBUST_ACCESS, &mut val);
245 val != 0
246
247 } else {
248 false
249 },
250
251 can_lose_context: if version >= &Version(Api::Gl, 4, 5) || extensions.gl_khr_robustness ||
252 extensions.gl_arb_robustness || extensions.gl_ext_robustness
253 {
254 let mut val = 0;
255 gl.GetIntegerv(gl::RESET_NOTIFICATION_STRATEGY, &mut val);
256
257 match val as gl::types::GLenum {
258 gl::LOSE_CONTEXT_ON_RESET => true,
259 gl::NO_RESET_NOTIFICATION => false,
260
261 0x31BE => false,
265
266 gl::NO_ERROR => false,
268
269 _ => unreachable!()
270 }
271
272 } else {
273 false
274 },
275
276 release_behavior: if extensions.gl_khr_context_flush_control {
277 let mut val = 0;
278 gl.GetIntegerv(gl::CONTEXT_RELEASE_BEHAVIOR, &mut val);
279
280 match val as gl::types::GLenum {
281 gl::NONE => ReleaseBehavior::None,
282 gl::CONTEXT_RELEASE_BEHAVIOR_FLUSH => ReleaseBehavior::Flush,
283 _ => unreachable!()
284 }
285
286 } else {
287 ReleaseBehavior::Flush
288 },
289
290 stereo: {
291 if version >= &Version(Api::Gl, 1, 0) {
292 let mut val: gl::types::GLboolean = 0;
293 gl.GetBooleanv(gl::STEREO, &mut val);
294 val != 0
295 } else {
296 false
297 }
298 },
299
300 srgb: {
301 if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_ext_framebuffer_srgb {
304 let mut fb = 0;
305 gl.GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut fb);
306 let attachment = if fb == 0 { gl::FRONT_LEFT } else { gl::COLOR_ATTACHMENT0 };
307 let mut value = 0;
308 gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, attachment,
309 gl::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
310 &mut value);
311 value as gl::types::GLenum == gl::SRGB
312
313 } else if extensions.gl_ext_framebuffer_srgb {
314 let mut value = 0;
315 gl.GetBooleanv(gl::FRAMEBUFFER_SRGB_CAPABLE_EXT, &mut value);
316 value != 0
317
318 } else {
319 false
320 }
321 },
322
323 depth_bits: {
324 let mut value = 0;
325
326 if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_arb_compatibility {
333 let mut fb = 0;
334 gl.GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut fb);
335 let attachment = if fb == 0 { gl::DEPTH } else { gl::DEPTH_ATTACHMENT };
336 let mut ty = 0;
337 gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, attachment,
338 gl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
339 &mut ty);
340
341 if ty as gl::types::GLenum == gl::NONE {
342 value = 0;
343 } else {
344 gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, attachment,
345 gl::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE,
346 &mut value);
347 }
348
349 } else {
350 gl.GetIntegerv(gl::DEPTH_BITS, &mut value);
351 };
352
353 match value {
354 0 => None,
355 v => Some(v as u16),
356 }
357 },
358
359 stencil_bits: {
360 let mut value = 0;
361
362 if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_arb_compatibility {
369 let mut fb = 0;
370 gl.GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut fb);
371 let attachment = if fb == 0 { gl::STENCIL } else { gl::STENCIL_ATTACHMENT };
372 let mut ty = 0;
373 gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, attachment,
374 gl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
375 &mut ty);
376
377 if ty as gl::types::GLenum == gl::NONE {
378 value = 0;
379 } else {
380 gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, attachment,
381 gl::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
382 &mut value);
383 }
384
385 } else {
386 gl.GetIntegerv(gl::STENCIL_BITS, &mut value);
387 };
388
389 match value {
390 0 => None,
391 v => Some(v as u16),
392 }
393 },
394
395 internal_formats_textures: get_internal_formats(gl, version, extensions, false),
396 internal_formats_renderbuffers: get_internal_formats(gl, version, extensions, true),
397
398 max_combined_texture_image_units: {
399 let mut val = 2;
400 gl.GetIntegerv(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mut val);
401
402 if renderer.contains("Radeon") {
405 val = cmp::min(val, 32);
406 }
407
408 val
409 },
410
411 max_texture_max_anisotropy: if !extensions.gl_ext_texture_filter_anisotropic {
412 None
413
414 } else {
415 Some({
416 let mut val = 0.0;
417 gl.GetFloatv(gl::MAX_TEXTURE_MAX_ANISOTROPY_EXT, &mut val);
418 val
419 })
420 },
421
422 max_texture_size: {
423 let mut val = 0;
424 gl.GetIntegerv(gl::MAX_TEXTURE_SIZE, &mut val);
425 val
426 },
427
428 max_texture_buffer_size: {
429 if version >= &Version(Api::Gl, 3, 0) || extensions.gl_arb_texture_buffer_object ||
430 extensions.gl_ext_texture_buffer_object || extensions.gl_oes_texture_buffer ||
431 extensions.gl_ext_texture_buffer
432 {
433 Some({
434 let mut val = 0;
435 gl.GetIntegerv(gl::MAX_TEXTURE_BUFFER_SIZE, &mut val);
436 val
437 })
438
439 } else {
440 None
441 }
442 },
443
444 max_viewport_dims: {
445 let mut val: [gl::types::GLint; 2] = [ 0, 0 ];
446 gl.GetIntegerv(gl::MAX_VIEWPORT_DIMS, val.as_mut_ptr());
447 (val[0], val[1])
448 },
449
450 max_draw_buffers: {
451 if version >= &Version(Api::Gl, 2, 0) ||
452 version >= &Version(Api::GlEs, 3, 0) ||
453 extensions.gl_ati_draw_buffers || extensions.gl_arb_draw_buffers
454 {
455 let mut val = 1;
456 gl.GetIntegerv(gl::MAX_DRAW_BUFFERS, &mut val);
457 val
458 } else {
459 1
460 }
461 },
462
463 max_patch_vertices: if version >= &Version(Api::Gl, 4, 0) ||
464 extensions.gl_arb_tessellation_shader
465 {
466 Some({
467 let mut val = 0;
468 gl.GetIntegerv(gl::MAX_PATCH_VERTICES, &mut val);
469 val
470 })
471
472 } else {
473 None
474 },
475
476 max_indexed_atomic_counter_buffer: if version >= &Version(Api::Gl, 4, 2) { let mut val = 0;
478 gl.GetIntegerv(gl::MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &mut val);
479 val
480 } else {
481 0
482 },
483
484 max_indexed_shader_storage_buffer: {
485 if version >= &Version(Api::Gl, 4, 3) || extensions.gl_arb_shader_storage_buffer_object { let mut val = 0;
487 gl.GetIntegerv(gl::MAX_SHADER_STORAGE_BUFFER_BINDINGS, &mut val);
488 val
489 } else {
490 0
491 }
492 },
493
494 max_indexed_transform_feedback_buffer: {
495 if version >= &Version(Api::Gl, 4, 0) || extensions.gl_arb_transform_feedback3 { let mut val = 0;
497 gl.GetIntegerv(gl::MAX_TRANSFORM_FEEDBACK_BUFFERS, &mut val);
498 val
499 } else if version >= &Version(Api::Gl, 3, 0) || extensions.gl_ext_transform_feedback {
500 let mut val = 0;
501 gl.GetIntegerv(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT, &mut val);
502 val
503 } else {
504 0
505 }
506 },
507
508 max_indexed_uniform_buffer: {
509 if version >= &Version(Api::Gl, 3, 1) || extensions.gl_arb_uniform_buffer_object { let mut val = 0;
511 gl.GetIntegerv(gl::MAX_UNIFORM_BUFFER_BINDINGS, &mut val);
512 val
513 } else {
514 0
515 }
516 },
517
518 max_compute_work_group_count: if version >= &Version(Api::Gl, 4, 3) ||
519 version >= &Version(Api::GlEs, 3, 1) ||
520 extensions.gl_arb_compute_shader
521 {
522 let mut val1 = 0;
523 let mut val2 = 0;
524 let mut val3 = 0;
525 gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 0, &mut val1);
526 gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 1, &mut val2);
527 gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 2, &mut val3);
528 (val1, val2, val3)
529
530 } else {
531 (0, 0, 0)
532 },
533
534 max_color_attachments: {
535 if version >= &Version(Api::Gl, 3, 0) || version >= &Version(Api::GlEs, 3, 0) ||
536 extensions.gl_arb_framebuffer_object || extensions.gl_ext_framebuffer_object ||
537 extensions.gl_nv_fbo_color_attachments
538 {
539 let mut val = 4;
540 gl.GetIntegerv(gl::MAX_COLOR_ATTACHMENTS, &mut val);
541 val
542 } else if version >= &Version(Api::GlEs, 2, 0) {
543 1
544 } else {
545 unreachable!()
547 }
548 },
549
550 max_framebuffer_width: {
551 if version >= &Version(Api::Gl, 4, 3) || version >= &Version(Api::GlEs, 3, 1) ||
552 extensions.gl_arb_framebuffer_no_attachments
553 {
554 let mut val = 0;
555 gl.GetIntegerv(gl::MAX_FRAMEBUFFER_WIDTH, &mut val);
556 Some(val)
557
558 } else {
559 None
560 }
561 },
562
563 max_framebuffer_height: {
564 if version >= &Version(Api::Gl, 4, 3) || version >= &Version(Api::GlEs, 3, 1) ||
565 extensions.gl_arb_framebuffer_no_attachments
566 {
567 let mut val = 0;
568 gl.GetIntegerv(gl::MAX_FRAMEBUFFER_HEIGHT, &mut val);
569 Some(val)
570
571 } else {
572 None
573 }
574 },
575
576 max_framebuffer_layers: {
577 if version >= &Version(Api::Gl, 4, 3) || version >= &Version(Api::GlEs, 3, 2) ||
578 extensions.gl_arb_framebuffer_no_attachments
579 {
580 let mut val = 0;
581 gl.GetIntegerv(gl::MAX_FRAMEBUFFER_LAYERS, &mut val);
582 Some(val)
583
584 } else {
585 None
586 }
587 },
588
589 max_framebuffer_samples: {
590 if version >= &Version(Api::Gl, 4, 3) || version >= &Version(Api::GlEs, 3, 1) ||
591 extensions.gl_arb_framebuffer_no_attachments
592 {
593 let mut val = 0;
594 gl.GetIntegerv(gl::MAX_FRAMEBUFFER_SAMPLES, &mut val);
595 Some(val)
596
597 } else {
598 None
599 }
600 },
601
602 renderer,
603 }
604}
605
606pub unsafe fn get_supported_glsl(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList)
616 -> Vec<Version>
617{
618 if version.0 == Api::GlEs {
621 let mut val = 0;
622 gl.GetBooleanv(gl::SHADER_COMPILER, &mut val);
623 if val == 0 {
624 return vec![];
625 }
626 }
627
628 if version >= &Version(Api::Gl, 4, 3) {
630 }
632
633 let mut result = Vec::with_capacity(8);
634
635 if version >= &Version(Api::GlEs, 2, 0) || version >= &Version(Api::Gl, 4, 1) ||
636 extensions.gl_arb_es2_compatibility
637 {
638 result.push(Version(Api::GlEs, 1, 0));
639 }
640
641 if version >= &Version(Api::GlEs, 3, 0) || version >= &Version(Api::Gl, 4, 3) ||
642 extensions.gl_arb_es3_compatibility
643 {
644 result.push(Version(Api::GlEs, 3, 0));
645 }
646
647 if version >= &Version(Api::GlEs, 3, 1) || version >= &Version(Api::Gl, 4, 5) ||
648 extensions.gl_arb_es3_1_compatibility
649 {
650 result.push(Version(Api::GlEs, 3, 1));
651 }
652
653 if version >= &Version(Api::GlEs, 3, 2) || extensions.gl_arb_es3_2_compatibility {
654 result.push(Version(Api::GlEs, 3, 2));
655 }
656
657 if version >= &Version(Api::Gl, 2, 0) && version <= &Version(Api::Gl, 3, 0) ||
658 extensions.gl_arb_compatibility
659 {
660 result.push(Version(Api::Gl, 1, 1));
661 }
662
663 if version >= &Version(Api::Gl, 2, 1) && version <= &Version(Api::Gl, 3, 0) ||
664 extensions.gl_arb_compatibility
665 {
666 result.push(Version(Api::Gl, 1, 2));
667 }
668
669 if version == &Version(Api::Gl, 3, 0) || extensions.gl_arb_compatibility {
670 result.push(Version(Api::Gl, 1, 3));
671 }
672
673 if version >= &Version(Api::Gl, 3, 1) {
674 result.push(Version(Api::Gl, 1, 4));
675 }
676
677 if version >= &Version(Api::Gl, 3, 2) {
678 result.push(Version(Api::Gl, 1, 5));
679 }
680
681 for &(major, minor) in &[(3, 3), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5)] {
682 if version >= &Version(Api::Gl, major, minor) {
683 result.push(Version(Api::Gl, major, minor));
684 }
685 }
686
687 result
688}
689
690pub fn get_internal_formats(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList,
692 renderbuffer: bool) -> HashMap<TextureFormat, FormatInfos, BuildHasherDefault<FnvHasher>>
693{
694 let dummy = {
696 struct DummyCaps<'a>(&'a Version, &'a ExtensionsList);
697 impl<'a> CapabilitiesSource for DummyCaps<'a> {
698 fn get_version(&self) -> &Version { self.0 }
699 fn get_extensions(&self) -> &ExtensionsList { self.1 }
700 fn get_capabilities(&self) -> &Capabilities { unreachable!() }
701 }
702 DummyCaps(version, extensions)
703 };
704
705 TextureFormat::get_formats_list().into_iter().filter_map(|format| {
706 if renderbuffer {
707 if !format.is_supported_for_renderbuffers(&dummy) {
708 return None;
709 }
710 } else if !format.is_supported_for_textures(&dummy) {
711 return None;
712 }
713
714 let infos = get_internal_format(gl, version, extensions, format, renderbuffer);
715 Some((format, infos))
716 }).collect()
717}
718
719pub fn get_internal_format(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList,
721 format: TextureFormat, renderbuffer: bool) -> FormatInfos
722{
723 let dummy = {
725 struct DummyCaps<'a>(&'a Version, &'a ExtensionsList);
726 impl<'a> CapabilitiesSource for DummyCaps<'a> {
727 fn get_version(&self) -> &Version { self.0 }
728 fn get_extensions(&self) -> &ExtensionsList { self.1 }
729 fn get_capabilities(&self) -> &Capabilities { unreachable!() }
730 }
731 DummyCaps(version, extensions)
732 };
733
734 unsafe {
735 let target = if renderbuffer { gl::RENDERBUFFER } else { gl::TEXTURE_2D_MULTISAMPLE };
736
737 let samples = if format.is_renderable(&dummy) &&
738 ((version >= &Version(Api::GlEs, 3, 0) && renderbuffer) ||
739 version >= &Version(Api::Gl, 4, 2) ||
740 extensions.gl_arb_internalformat_query)
741 {
742 let mut num = 0;
743 gl.GetInternalformativ(target, format.to_glenum(), gl::NUM_SAMPLE_COUNTS, 1, &mut num);
744
745 if num >= 1 {
746 let mut formats = Vec::with_capacity(num as usize);
747 gl.GetInternalformativ(target, format.to_glenum(), gl::SAMPLES, num,
748 formats.as_mut_ptr());
749 formats.set_len(num as usize);
750 Some(formats)
751
752 } else {
753 Some(Vec::new())
754 }
755
756 } else {
757 None
758 };
759
760 FormatInfos {
761 multisamples: samples,
762 }
763 }
764}