gfx_backend_gl/
info.rs

1use crate::{Error, GlContainer, MAX_COLOR_ATTACHMENTS};
2use glow::HasContext;
3use hal::{DynamicStates, Features, Limits, PerformanceCaveats, PhysicalDeviceProperties};
4use std::{collections::HashSet, fmt, str};
5
6/// A version number for a specific component of an OpenGL implementation
7#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
8pub struct Version {
9    pub major: u32,
10    pub minor: u32,
11    pub is_embedded: bool,
12    pub revision: Option<u32>,
13    pub vendor_info: String,
14}
15
16impl Version {
17    /// Create a new OpenGL version number
18    pub fn new(major: u32, minor: u32, revision: Option<u32>, vendor_info: String) -> Self {
19        Version {
20            major: major,
21            minor: minor,
22            is_embedded: false,
23            revision: revision,
24            vendor_info,
25        }
26    }
27    /// Create a new OpenGL ES version number
28    pub fn new_embedded(major: u32, minor: u32, vendor_info: String) -> Self {
29        Version {
30            major,
31            minor,
32            is_embedded: true,
33            revision: None,
34            vendor_info,
35        }
36    }
37
38    /// Get a tuple of (major, minor) versions
39    pub fn tuple(&self) -> (u32, u32) {
40        (self.major, self.minor)
41    }
42
43    /// According to the OpenGL specification, the version information is
44    /// expected to follow the following syntax:
45    ///
46    /// ~~~bnf
47    /// <major>       ::= <number>
48    /// <minor>       ::= <number>
49    /// <revision>    ::= <number>
50    /// <vendor-info> ::= <string>
51    /// <release>     ::= <major> "." <minor> ["." <release>]
52    /// <version>     ::= <release> [" " <vendor-info>]
53    /// ~~~
54    ///
55    /// Note that this function is intentionally lenient in regards to parsing,
56    /// and will try to recover at least the first two version numbers without
57    /// resulting in an `Err`.
58    /// # Notes
59    /// `WebGL 2` version returned as `OpenGL ES 3.0`
60    pub fn parse(mut src: &str) -> Result<Version, &str> {
61        let webgl_sig = "WebGL ";
62        // According to the WebGL specification
63        // VERSION	WebGL<space>1.0<space><vendor-specific information>
64        // SHADING_LANGUAGE_VERSION	WebGL<space>GLSL<space>ES<space>1.0<space><vendor-specific information>
65        let is_webgl = src.starts_with(webgl_sig);
66        let is_es = if is_webgl {
67            let pos = src.rfind(webgl_sig).unwrap_or(0);
68            src = &src[pos + webgl_sig.len()..];
69            true
70        } else {
71            let es_sig = " ES ";
72            match src.rfind(es_sig) {
73                Some(pos) => {
74                    src = &src[pos + es_sig.len()..];
75                    true
76                }
77                None => false,
78            }
79        };
80
81        let glsl_es_sig = "GLSL ES ";
82        let is_glsl = match src.find(glsl_es_sig) {
83            Some(pos) => {
84                src = &src[pos + glsl_es_sig.len()..];
85                true
86            }
87            None => false,
88        };
89
90        let (version, vendor_info) = match src.find(' ') {
91            Some(i) => (&src[..i], src[i + 1..].to_string()),
92            None => (src, String::new()),
93        };
94
95        // TODO: make this even more lenient so that we can also accept
96        // `<major> "." <minor> [<???>]`
97        let mut it = version.split('.');
98        let major = it.next().and_then(|s| s.parse().ok());
99        let minor = it.next().and_then(|s| {
100            let trimmed = if s.starts_with('0') {
101                "0"
102            } else {
103                s.trim_end_matches('0')
104            };
105            trimmed.parse().ok()
106        });
107        let revision = if is_webgl {
108            None
109        } else {
110            it.next().and_then(|s| s.parse().ok())
111        };
112
113        match (major, minor, revision) {
114            (Some(major), Some(minor), revision) => Ok(Version {
115                // Return WebGL 2.0 version as OpenGL ES 3.0
116                major: if is_webgl && !is_glsl {
117                    major + 1
118                } else {
119                    major
120                },
121                minor,
122                is_embedded: is_es,
123                revision,
124                vendor_info,
125            }),
126            (_, _, _) => Err(src),
127        }
128    }
129}
130
131impl fmt::Debug for Version {
132    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        match (
134            self.major,
135            self.minor,
136            self.revision,
137            self.vendor_info.as_str(),
138        ) {
139            (major, minor, Some(revision), "") => write!(f, "{}.{}.{}", major, minor, revision),
140            (major, minor, None, "") => write!(f, "{}.{}", major, minor),
141            (major, minor, Some(revision), vendor_info) => {
142                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info)
143            }
144            (major, minor, None, vendor_info) => write!(f, "{}.{}, {}", major, minor, vendor_info),
145        }
146    }
147}
148
149fn get_string(gl: &GlContainer, name: u32) -> Result<String, Error> {
150    let value = unsafe { gl.get_parameter_string(name) };
151    let err = Error::from_error_code(unsafe { gl.get_error() });
152    if err != Error::NoError {
153        Err(err)
154    } else {
155        Ok(value)
156    }
157}
158fn get_usize(gl: &GlContainer, name: u32) -> Result<usize, Error> {
159    let value = unsafe { gl.get_parameter_i32(name) };
160    let err = Error::from_error_code(unsafe { gl.get_error() });
161    if err != Error::NoError {
162        Err(err)
163    } else {
164        Ok(value as usize)
165    }
166}
167fn get_u64(gl: &GlContainer, name: u32) -> Result<u64, Error> {
168    let value = unsafe { gl.get_parameter_i32(name) };
169    let err = Error::from_error_code(unsafe { gl.get_error() });
170    if err != Error::NoError {
171        Err(err)
172    } else {
173        Ok(value as u64)
174    }
175}
176
177/// A unique platform identifier that does not change between releases
178#[derive(Clone, Eq, PartialEq, Debug)]
179pub struct PlatformName {
180    /// The company responsible for the OpenGL implementation
181    pub vendor: String,
182    /// The name of the renderer
183    pub renderer: String,
184}
185
186impl PlatformName {
187    fn get(gl: &GlContainer) -> Self {
188        PlatformName {
189            vendor: get_string(gl, glow::VENDOR).unwrap_or_default(),
190            renderer: get_string(gl, glow::RENDERER).unwrap_or_default(),
191        }
192    }
193}
194
195/// Private capabilities that don't need to be exposed.
196/// The affect the implementation code paths but not the
197/// provided API surface.
198#[derive(Debug)]
199pub struct PrivateCaps {
200    /// VAO support
201    pub vertex_array: bool,
202    /// FBO support
203    pub framebuffer: bool,
204    /// FBO support to call `glFramebufferTexture`
205    pub framebuffer_texture: bool,
206    /// If true, then buffers used as ELEMENT_ARRAY_BUFFER may be created / initialized / used as
207    /// other targets, if false they must not be mixed with other targets.
208    pub index_buffer_role_change: bool,
209    pub buffer_storage: bool,
210    pub image_storage: bool,
211    pub clear_buffer: bool,
212    pub program_interface: bool,
213    pub frag_data_location: bool,
214    pub sync: bool,
215    /// Whether to emulate memory mapping (`glMapBuffer`/`glMapBufferRange`)
216    /// when it is not available:
217    /// - In OpenGL ES 2 it may be available behind optional extensions
218    /// - In WebGL 1 and WebGL 2 it is never available
219    /// - In OpenGL, currently required to get copies from/to buffers working:
220    /// https://github.com/gfx-rs/gfx/issues/3453
221    pub emulate_map: bool,
222    /// Whether f64 precision is supported for depth ranges
223    pub depth_range_f64_precision: bool,
224    /// Whether draw buffers are supported
225    pub draw_buffers: bool,
226    /// Whether separate color masks per output buffer are supported.
227    pub per_slot_color_mask: bool,
228    /// Reading from textures into CPU memory is supported.
229    pub get_tex_image: bool,
230    /// Inserting memory barriers.
231    pub memory_barrier: bool,
232}
233
234/// OpenGL implementation information
235#[derive(Debug)]
236pub struct Info {
237    /// The platform identifier
238    pub platform_name: PlatformName,
239    /// The OpenGL API version number
240    pub version: Version,
241    /// The GLSL version number
242    pub shading_language: Version,
243    /// The extensions supported by the implementation
244    pub extensions: HashSet<String>,
245}
246
247bitflags::bitflags! {
248    /// Flags for features that are required for Vulkan but may not
249    /// be supported by legacy backends (GL/DX11).
250    pub struct LegacyFeatures: u32 {
251        /// Support indirect drawing and dispatching.
252        const INDIRECT_EXECUTION = 0x00000001;
253        /// Support instanced drawing.
254        const DRAW_INSTANCED = 0x00000002;
255        /// Support offsets for instanced drawing with base instance.
256        const DRAW_INSTANCED_BASE = 0x00000004;
257        /// Support indexed drawing with base vertex.
258        const DRAW_INDEXED_BASE = 0x00000008;
259        /// Support indexed, instanced drawing.
260        const DRAW_INDEXED_INSTANCED = 0x00000010;
261        /// Support indexed, instanced drawing with base vertex only.
262        const DRAW_INDEXED_INSTANCED_BASE_VERTEX = 0x00000020;
263        /// Support base vertex offset for indexed drawing.
264        const VERTEX_BASE = 0x00000080;
265        /// Support sRGB textures and rendertargets.
266        const SRGB_COLOR = 0x00000100;
267        /// Support constant buffers.
268        const CONSTANT_BUFFER = 0x00000200;
269        /// Support unordered-access views.
270        const UNORDERED_ACCESS_VIEW = 0x00000400;
271        /// Support accelerated buffer copy.
272        const COPY_BUFFER = 0x00000800;
273        /// Support separation of textures and samplers.
274        const SAMPLER_OBJECTS = 0x00001000;
275        /// Support explicit layouts in shader.
276        const EXPLICIT_LAYOUTS_IN_SHADER = 0x00002000;
277        /// Support instanced input rate on attribute binding.
278        const INSTANCED_ATTRIBUTE_BINDING = 0x00004000;
279    }
280}
281
282#[derive(Copy, Clone)]
283pub enum Requirement<'a> {
284    Core(u32, u32),
285    Es(u32, u32),
286    Ext(&'a str),
287}
288
289impl Info {
290    fn get(gl: &GlContainer) -> Info {
291        let platform_name = PlatformName::get(gl);
292        let raw_version = get_string(gl, glow::VERSION).unwrap_or_default();
293        let version = Version::parse(&raw_version).unwrap();
294        let shading_language = {
295            let raw_shader_version =
296                get_string(gl, glow::SHADING_LANGUAGE_VERSION).unwrap_or_default();
297            Version::parse(&raw_shader_version).unwrap()
298        };
299
300        // TODO: Use separate path for WebGL extensions in `glow` somehow
301        // Perhaps automatic fallback for NUM_EXTENSIONS to EXTENSIONS on native
302        let extensions = if crate::is_webgl() {
303            HashSet::new()
304        } else if (version >= Version::new(3, 0, None, String::from("")))
305            || (version >= Version::new_embedded(3, 0, String::from("")))
306        {
307            let num_exts = get_usize(gl, glow::NUM_EXTENSIONS).unwrap();
308            (0..num_exts)
309                .map(|i| unsafe { gl.get_parameter_indexed_string(glow::EXTENSIONS, i as u32) })
310                .collect()
311        } else {
312            // Fallback
313            get_string(gl, glow::EXTENSIONS)
314                .unwrap_or_else(|_| String::from(""))
315                .split(' ')
316                .map(|s| s.to_string())
317                .collect()
318        };
319
320        Info {
321            platform_name,
322            version,
323            shading_language,
324            extensions,
325        }
326    }
327
328    pub fn is_version_supported(&self, major: u32, minor: u32) -> bool {
329        !self.version.is_embedded
330            && self.version >= Version::new(major, minor, None, String::from(""))
331    }
332
333    pub fn is_embedded_version_supported(&self, major: u32, minor: u32) -> bool {
334        self.version.is_embedded
335            && self.version >= Version::new_embedded(major, minor, String::from(""))
336    }
337
338    /// Returns `true` if the implementation supports the extension
339    pub fn is_extension_supported(&self, s: &str) -> bool {
340        self.extensions.contains(s)
341    }
342
343    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &str) -> bool {
344        self.is_version_supported(major, minor) || self.is_extension_supported(ext)
345    }
346
347    pub fn is_any_extension_supported(&self, exts: &[String]) -> bool {
348        exts.iter().any(|e| self.extensions.contains(e))
349    }
350
351    pub fn is_supported(&self, requirements: &[Requirement]) -> bool {
352        use self::Requirement::*;
353        requirements.iter().any(|r| match *r {
354            Core(major, minor) => self.is_version_supported(major, minor),
355            Es(major, minor) => self.is_embedded_version_supported(major, minor),
356            Ext(extension) => self.is_extension_supported(extension),
357        })
358    }
359}
360
361/// This structure checks whether a given image format is whitelisted to be used
362/// or not in the backend.
363///
364/// Unlike with Vulkan, OpenGL gives its users no easy way to query for valid
365/// image formats. Instead, it relies heavily on semantics, which are laid out
366/// in its specifications and function documentations.
367///
368/// This structure is intended to condense all of the supported formats into a
369/// single queryable location, acquired once when the adapter is created.
370#[derive(Debug)]
371pub enum TextureFormatFilter {
372    /// This filter names a set of allowed combinations. All combinations not
373    /// explicitly whitelisted will be reported as not available by the check
374    /// function.
375    Whitelist { whitelist: HashSet<(u32, u32, u32)> },
376    /// This filter is permissive, i.e. it reports all combinations as
377    /// available. Currently intended for use in the core profile.
378    Permissive,
379}
380impl TextureFormatFilter {
381    /// This is the fixed, spec-defined list of all triplets in the form
382    /// `(Internal Format, Format, Type)` that are supported by OpenGL ES 3 and
383    /// WebGL for image creation via the `glTexImage` family of functions.
384    ///
385    /// Combinations of these parameters other than the ones in this table are
386    /// not supported by the core specifications. Though that does not mean they
387    /// can't be used or can't work under any circumstance. What it does mean is
388    /// that using anything outside this list is relying on permissive drivers
389    /// to not trigger undefined behavior.
390    ///
391    /// This is a one to one copy of the tables provided to us in
392    /// [the documentation of `glTexImage2D`], which is the same table as the
393    /// for `glTexImage3D`. The contents of the table are copied pretty much
394    /// verbatim in order to facilitate maintenance.
395    ///
396    /// [the documentation of `glTexImage2D`]: https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glTexImage2D.xhtml
397    const ES3_TABLE: &'static [(u32, u32, u32)] = &[
398        /* Taken from Table 1. Unsized Internal Formats. */
399        (glow::RGB, glow::RGB, glow::UNSIGNED_BYTE),
400        (glow::RGB, glow::RGB, glow::UNSIGNED_SHORT_5_6_5),
401        (glow::RGBA, glow::RGBA, glow::UNSIGNED_BYTE),
402        (glow::RGBA, glow::RGBA, glow::UNSIGNED_SHORT_4_4_4_4),
403        (glow::RGBA, glow::RGBA, glow::UNSIGNED_SHORT_5_5_5_1),
404        (
405            glow::LUMINANCE_ALPHA,
406            glow::LUMINANCE_ALPHA,
407            glow::UNSIGNED_BYTE,
408        ),
409        (glow::LUMINANCE, glow::LUMINANCE, glow::UNSIGNED_BYTE),
410        (glow::ALPHA, glow::ALPHA, glow::UNSIGNED_BYTE),
411        /* Taken from Table 2. Sized Internal Formats. */
412        (glow::R8, glow::RED, glow::UNSIGNED_BYTE),
413        (glow::R8_SNORM, glow::RED, glow::BYTE),
414        (glow::R16F, glow::RED, glow::HALF_FLOAT),
415        (glow::R16F, glow::RED, glow::FLOAT),
416        (glow::R32F, glow::RED, glow::FLOAT),
417        (glow::R8UI, glow::RED_INTEGER, glow::UNSIGNED_BYTE),
418        (glow::R8I, glow::RED_INTEGER, glow::BYTE),
419        (glow::R16UI, glow::RED_INTEGER, glow::UNSIGNED_SHORT),
420        (glow::R16I, glow::RED_INTEGER, glow::SHORT),
421        (glow::R32UI, glow::RED_INTEGER, glow::UNSIGNED_INT),
422        (glow::R32I, glow::RED_INTEGER, glow::INT),
423        (glow::RG8, glow::RG, glow::UNSIGNED_BYTE),
424        (glow::RG8_SNORM, glow::RG, glow::BYTE),
425        (glow::RG16F, glow::RG, glow::HALF_FLOAT),
426        (glow::RG16F, glow::RG, glow::FLOAT),
427        (glow::RG32F, glow::RG, glow::FLOAT),
428        (glow::RG8UI, glow::RG_INTEGER, glow::UNSIGNED_BYTE),
429        (glow::RG8I, glow::RG_INTEGER, glow::BYTE),
430        (glow::RG16UI, glow::RG_INTEGER, glow::UNSIGNED_SHORT),
431        (glow::RG16I, glow::RG_INTEGER, glow::SHORT),
432        (glow::RG32UI, glow::RG_INTEGER, glow::UNSIGNED_INT),
433        (glow::RG32I, glow::RG_INTEGER, glow::INT),
434        (glow::RGB8, glow::RGB, glow::UNSIGNED_BYTE),
435        (glow::SRGB8, glow::RGB, glow::UNSIGNED_BYTE),
436        (glow::RGB565, glow::RGB, glow::UNSIGNED_BYTE),
437        (glow::RGB565, glow::RGB, glow::UNSIGNED_SHORT_5_6_5),
438        (glow::RGB8_SNORM, glow::RGB, glow::BYTE),
439        (
440            glow::R11F_G11F_B10F,
441            glow::RGB,
442            glow::UNSIGNED_INT_10F_11F_11F_REV,
443        ),
444        (glow::R11F_G11F_B10F, glow::RGB, glow::HALF_FLOAT),
445        (glow::R11F_G11F_B10F, glow::RGB, glow::FLOAT),
446        (glow::RGB9_E5, glow::RGB, glow::UNSIGNED_INT_5_9_9_9_REV),
447        (glow::RGB9_E5, glow::RGB, glow::HALF_FLOAT),
448        (glow::RGB9_E5, glow::RGB, glow::FLOAT),
449        (glow::RGB16F, glow::RGB, glow::HALF_FLOAT),
450        (glow::RGB16F, glow::RGB, glow::FLOAT),
451        (glow::RGB32F, glow::RGB, glow::FLOAT),
452        (glow::RGB8UI, glow::RGB_INTEGER, glow::UNSIGNED_BYTE),
453        (glow::RGB8I, glow::RGB_INTEGER, glow::BYTE),
454        (glow::RGB16UI, glow::RGB_INTEGER, glow::UNSIGNED_SHORT),
455        (glow::RGB16I, glow::RGB_INTEGER, glow::SHORT),
456        (glow::RGB32UI, glow::RGB_INTEGER, glow::UNSIGNED_INT),
457        (glow::RGB32I, glow::RGB_INTEGER, glow::INT),
458        (glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE),
459        (glow::SRGB8_ALPHA8, glow::RGBA, glow::UNSIGNED_BYTE),
460        (glow::RGBA8_SNORM, glow::RGBA, glow::BYTE),
461        (glow::RGB5_A1, glow::RGBA, glow::UNSIGNED_BYTE),
462        (glow::RGB5_A1, glow::RGBA, glow::UNSIGNED_SHORT_5_5_5_1),
463        (glow::RGB5_A1, glow::RGBA, glow::UNSIGNED_INT_2_10_10_10_REV),
464        (glow::RGBA4, glow::RGBA, glow::UNSIGNED_BYTE),
465        (glow::RGBA4, glow::RGBA, glow::UNSIGNED_SHORT_4_4_4_4),
466        (
467            glow::RGB10_A2,
468            glow::RGBA,
469            glow::UNSIGNED_INT_2_10_10_10_REV,
470        ),
471        (glow::RGBA16F, glow::RGBA, glow::HALF_FLOAT),
472        (glow::RGBA16F, glow::RGBA, glow::FLOAT),
473        (glow::RGBA32F, glow::RGBA, glow::FLOAT),
474        (glow::RGBA8UI, glow::RGBA_INTEGER, glow::UNSIGNED_BYTE),
475        (glow::RGBA8I, glow::RGBA_INTEGER, glow::BYTE),
476        (
477            glow::RGB10_A2UI,
478            glow::RGBA_INTEGER,
479            glow::UNSIGNED_INT_2_10_10_10_REV,
480        ),
481        (glow::RGBA16UI, glow::RGBA_INTEGER, glow::UNSIGNED_SHORT),
482        (glow::RGBA16I, glow::RGBA_INTEGER, glow::SHORT),
483        (glow::RGBA32I, glow::RGBA_INTEGER, glow::INT),
484        (glow::RGBA32UI, glow::RGBA_INTEGER, glow::UNSIGNED_INT),
485        (
486            glow::DEPTH_COMPONENT16,
487            glow::DEPTH_COMPONENT,
488            glow::UNSIGNED_SHORT,
489        ),
490        (
491            glow::DEPTH_COMPONENT16,
492            glow::DEPTH_COMPONENT,
493            glow::UNSIGNED_INT,
494        ),
495        (
496            glow::DEPTH_COMPONENT24,
497            glow::DEPTH_COMPONENT,
498            glow::UNSIGNED_INT,
499        ),
500        (glow::DEPTH_COMPONENT32F, glow::DEPTH_COMPONENT, glow::FLOAT),
501        (
502            glow::DEPTH24_STENCIL8,
503            glow::DEPTH_STENCIL,
504            glow::UNSIGNED_INT_24_8,
505        ),
506        (
507            glow::DEPTH32F_STENCIL8,
508            glow::DEPTH_STENCIL,
509            glow::FLOAT_32_UNSIGNED_INT_24_8_REV,
510        ),
511        (
512            glow::STENCIL_INDEX8,
513            glow::STENCIL_INDEX,
514            glow::UNSIGNED_BYTE,
515        ),
516    ];
517
518    /// Fixed list of format and type combinations supported by both OpenGL ES 1
519    /// and OpenGL ES 2. These values stayed the same between these two
520    /// versions, so we can just lump them together.
521    ///
522    /// This is a one to one copy of the tables provided to us in
523    /// [the documentation of `glTexImage2D`], which is the same table as the
524    /// for `glTexImage3D`. The contents of the table are copied pretty much
525    /// verbatim in order to facilitate maintenance.
526    ///
527    /// [the documentation of `glTexImage2D`]: https://khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/
528    const ES1_ES2_TABLE: &'static [(u32, u32, u32)] = &[
529        (glow::ALPHA, glow::ALPHA, glow::UNSIGNED_BYTE),
530        (glow::RGB, glow::RGB, glow::UNSIGNED_BYTE),
531        (glow::RGB, glow::RGB, glow::UNSIGNED_SHORT_5_6_5),
532        (glow::RGBA, glow::RGBA, glow::UNSIGNED_BYTE),
533        (glow::RGBA, glow::RGBA, glow::UNSIGNED_SHORT_4_4_4_4),
534        (glow::RGBA, glow::RGBA, glow::UNSIGNED_SHORT_5_5_5_1),
535        (glow::LUMINANCE, glow::LUMINANCE, glow::UNSIGNED_BYTE),
536        (
537            glow::LUMINANCE_ALPHA,
538            glow::LUMINANCE_ALPHA,
539            glow::UNSIGNED_BYTE,
540        ),
541    ];
542
543    /// Creates a new filter for OpenGL ES 3.
544    fn new_es3() -> Self {
545        Self::Whitelist {
546            whitelist: {
547                let mut whitelist = HashSet::<(u32, u32, u32)>::default();
548                whitelist.extend(Self::ES3_TABLE);
549
550                whitelist
551            },
552        }
553    }
554
555    /// Creates a new filter for OpenGL ES 2 and OpenGL ES 1.
556    fn new_es1_es2() -> Self {
557        Self::Whitelist {
558            whitelist: {
559                let mut whitelist = HashSet::<(u32, u32, u32)>::default();
560                whitelist.extend(Self::ES1_ES2_TABLE);
561
562                whitelist
563            },
564        }
565    }
566
567    /// Creates a new, permissive filter.
568    fn new_permissive() -> Self {
569        Self::Permissive
570    }
571
572    /// This function checks whether a given format description is allowed by
573    /// this filter.
574    pub fn check(&self, internal_format: u32, format: u32, type_: u32) -> bool {
575        match self {
576            Self::Whitelist { whitelist } => whitelist.contains(&(internal_format, format, type_)),
577            Self::Permissive => true,
578        }
579    }
580}
581
582/// Load the information pertaining to the driver and the corresponding device
583/// capabilities.
584pub(crate) fn query_all(
585    gl: &GlContainer,
586) -> (
587    Info,
588    Features,
589    LegacyFeatures,
590    PhysicalDeviceProperties,
591    PrivateCaps,
592    TextureFormatFilter,
593) {
594    use self::Requirement::*;
595    let info = Info::get(gl);
596    let max_texture_size = get_usize(gl, glow::MAX_TEXTURE_SIZE).unwrap_or(64) as u32;
597    let max_samples = get_usize(gl, glow::MAX_SAMPLES).unwrap_or(8);
598    let max_samples_mask = (max_samples * 2 - 1) as u8;
599    let max_texel_elements = if crate::is_webgl() {
600        0
601    } else {
602        get_usize(gl, glow::MAX_TEXTURE_BUFFER_SIZE).unwrap_or(0)
603    };
604    let min_storage_buffer_offset_alignment = if crate::is_webgl() {
605        256
606    } else {
607        get_u64(gl, glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT).unwrap_or(256)
608    };
609
610    let mut limits = Limits {
611        max_image_1d_size: max_texture_size,
612        max_image_2d_size: max_texture_size,
613        max_image_3d_size: max_texture_size,
614        max_image_cube_size: max_texture_size,
615        max_image_array_layers: get_usize(gl, glow::MAX_ARRAY_TEXTURE_LAYERS).unwrap_or(1) as u16,
616        max_texel_elements,
617        max_viewports: 1,
618        optimal_buffer_copy_offset_alignment: 1,
619        optimal_buffer_copy_pitch_alignment: 1,
620        min_texel_buffer_offset_alignment: 1,
621        min_uniform_buffer_offset_alignment: get_u64(gl, glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT)
622            .unwrap_or(1024),
623        min_storage_buffer_offset_alignment,
624        framebuffer_color_sample_counts: max_samples_mask,
625        non_coherent_atom_size: 1,
626        max_color_attachments: get_usize(gl, glow::MAX_COLOR_ATTACHMENTS)
627            .unwrap_or(1)
628            .min(MAX_COLOR_ATTACHMENTS),
629        ..Limits::default()
630    };
631
632    if info.is_supported(&[Core(4, 0), Ext("GL_ARB_tessellation_shader")]) {
633        limits.max_patch_size = get_usize(gl, glow::MAX_PATCH_VERTICES).unwrap_or(0) as _;
634    }
635    if info.is_supported(&[Core(4, 1)]) {
636        // TODO: extension
637        limits.max_viewports = get_usize(gl, glow::MAX_VIEWPORTS).unwrap_or(0);
638    }
639
640    //TODO: technically compute is exposed in Es(3, 1), but GLES requires 3.2
641    // for any storage buffers. We need to investigate if this requirement
642    // can be lowered.
643    if info.is_supported(&[Core(4, 3), Es(3, 2), Ext("GL_ARB_compute_shader")]) {
644        for (i, (count, size)) in limits
645            .max_compute_work_group_count
646            .iter_mut()
647            .zip(limits.max_compute_work_group_size.iter_mut())
648            .enumerate()
649        {
650            unsafe {
651                *count =
652                    gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_COUNT, i as _) as u32;
653                *size =
654                    gl.get_parameter_indexed_i32(glow::MAX_COMPUTE_WORK_GROUP_SIZE, i as _) as u32;
655            }
656        }
657    }
658
659    let mut features = Features::NDC_Y_UP | Features::MUTABLE_COMPARISON_SAMPLER;
660    // TODO: Fill out downlevel features correctly.
661    let mut downlevel = hal::DownlevelProperties::all_enabled();
662    // TODO: Merge downlevel/legacy features?
663    let mut legacy = LegacyFeatures::empty();
664
665    if info.is_supported(&[
666        Core(4, 6),
667        Ext("GL_ARB_texture_filter_anisotropic"),
668        Ext("GL_EXT_texture_filter_anisotropic"),
669    ]) {
670        features |= Features::SAMPLER_ANISOTROPY;
671    }
672    if info.is_supported(&[Core(4, 2), Es(3, 1)]) {
673        legacy |= LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER;
674    }
675    if info.is_supported(&[Core(3, 3), Es(3, 0), Ext("GL_ARB_instanced_arrays")]) {
676        features |= Features::INSTANCE_RATE;
677    }
678    if info.is_supported(&[Core(3, 3)]) {
679        // TODO: extension
680        features |= Features::SAMPLER_MIP_LOD_BIAS;
681    }
682    if info.is_supported(&[Core(2, 1)]) {
683        features |= Features::SAMPLER_BORDER_COLOR;
684    }
685    if info.is_supported(&[Core(4, 4), Ext("ARB_texture_mirror_clamp_to_edge")]) {
686        features |= Features::SAMPLER_MIRROR_CLAMP_EDGE;
687    }
688    if info.is_supported(&[Core(4, 0), Es(3, 2), Ext("GL_EXT_draw_buffers2")]) && !crate::is_webgl()
689    {
690        features |= Features::INDEPENDENT_BLENDING;
691    }
692
693    // TODO
694    if false && info.is_supported(&[Core(4, 3), Es(3, 1)]) {
695        // TODO: extension
696        legacy |= LegacyFeatures::INDIRECT_EXECUTION;
697    }
698    if info.is_supported(&[Core(3, 1), Es(3, 0), Ext("GL_ARB_draw_instanced")]) {
699        legacy |= LegacyFeatures::DRAW_INSTANCED;
700    }
701    if info.is_supported(&[Core(4, 2), Ext("GL_ARB_base_instance")]) {
702        legacy |= LegacyFeatures::DRAW_INSTANCED_BASE;
703    }
704    if info.is_supported(&[Core(3, 2)]) {
705        // TODO: extension
706        legacy |= LegacyFeatures::DRAW_INDEXED_BASE;
707    }
708    if info.is_supported(&[Core(3, 1), Es(3, 0)]) {
709        // TODO: extension
710        legacy |= LegacyFeatures::DRAW_INDEXED_INSTANCED;
711    }
712    if info.is_supported(&[Core(3, 2)]) {
713        // TODO: extension
714        legacy |= LegacyFeatures::DRAW_INDEXED_INSTANCED_BASE_VERTEX;
715    }
716    if info.is_supported(&[
717        Core(3, 2),
718        Es(3, 2),
719        Ext("GL_ARB_draw_elements_base_vertex"),
720    ]) {
721        legacy |= LegacyFeatures::VERTEX_BASE;
722    }
723    if info.is_supported(&[
724        Core(3, 1),
725        Es(3, 0),
726        Ext("GL_ARB_framebuffer_sRGB"),
727        Ext("GL_EXT_sRGB"),
728    ]) {
729        legacy |= LegacyFeatures::SRGB_COLOR;
730    }
731    if info.is_supported(&[Core(3, 1), Es(3, 0), Ext("GL_ARB_uniform_buffer_object")]) {
732        legacy |= LegacyFeatures::CONSTANT_BUFFER;
733    }
734    if info.is_supported(&[Core(4, 0)]) {
735        // TODO: extension
736        legacy |= LegacyFeatures::UNORDERED_ACCESS_VIEW;
737    }
738    if info.is_supported(&[
739        Core(3, 1),
740        Es(3, 0),
741        Ext("GL_ARB_copy_buffer"),
742        Ext("GL_NV_copy_buffer"),
743    ]) {
744        legacy |= LegacyFeatures::COPY_BUFFER;
745    }
746    if info.is_supported(&[Core(3, 3), Es(3, 0), Ext("GL_ARB_sampler_objects")]) {
747        legacy |= LegacyFeatures::SAMPLER_OBJECTS;
748    }
749    if info.is_supported(&[Core(3, 3), Es(3, 0)]) {
750        legacy |= LegacyFeatures::INSTANCED_ATTRIBUTE_BINDING;
751    }
752
753    let mut performance_caveats = PerformanceCaveats::empty();
754    //TODO: extension
755    if !info.is_supported(&[Core(4, 2)]) {
756        performance_caveats |= PerformanceCaveats::BASE_VERTEX_INSTANCE_DRAWING;
757    }
758    let properties = PhysicalDeviceProperties {
759        limits,
760        performance_caveats,
761        dynamic_pipeline_states: DynamicStates::all(),
762        ..PhysicalDeviceProperties::default()
763    };
764
765    let buffer_storage = info.is_supported(&[
766        Core(4, 4),
767        Ext("GL_ARB_buffer_storage"),
768        Ext("GL_EXT_buffer_storage"),
769    ]);
770    // See https://github.com/gfx-rs/gfx/issues/3453
771    let emulate_map = crate::is_webgl() || !buffer_storage;
772
773    let private = PrivateCaps {
774        vertex_array: info.is_supported(&[Core(3, 0), Es(3, 0), Ext("GL_ARB_vertex_array_object")]),
775        // TODO && gl.GenVertexArrays.is_loaded(),
776        framebuffer: info.is_supported(&[Core(3, 0), Es(2, 0), Ext("GL_ARB_framebuffer_object")]),
777        // TODO && gl.GenFramebuffers.is_loaded(),
778        framebuffer_texture: info.is_supported(&[Core(3, 0)]), //TODO: double check
779        // `WebGL` Note: buffers bound to non ELEMENT_ARRAY_BUFFER targets can not be bound to ELEMENT_ARRAY_BUFFER target
780        index_buffer_role_change: info.is_supported(&[Core(2, 0), Es(2, 0)]) && !crate::is_webgl(),
781        image_storage: info.is_supported(&[Core(4, 2), Es(3, 0), Ext("GL_ARB_texture_storage")]),
782        buffer_storage,
783        clear_buffer: info.is_supported(&[Core(3, 0), Es(3, 0)]),
784        program_interface: info.is_supported(&[Core(4, 3), Ext("GL_ARB_program_interface_query")]),
785        frag_data_location: !info.version.is_embedded,
786        sync: info.is_supported(&[Core(3, 2), Es(3, 0), Ext("GL_ARB_sync")]), // TODO
787        emulate_map,
788        depth_range_f64_precision: !info.version.is_embedded, // TODO
789        draw_buffers: info.is_supported(&[Core(2, 0), Es(3, 0)]),
790        per_slot_color_mask: info.is_supported(&[Core(3, 0)]),
791        get_tex_image: !info.version.is_embedded,
792        memory_barrier: info.is_supported(&[Core(4, 2), Es(3, 1)]),
793    };
794
795    let filter = if info.is_supported(&[Es(3, 0)]) {
796        /* Use the OpenGL ES 3 format filter. */
797        TextureFormatFilter::new_es3()
798    } else if info.is_supported(&[Es(1, 0)]) {
799        /* Use the OpenGL ES 1 and OpenGL ES 2 format filter. */
800        TextureFormatFilter::new_es1_es2()
801    } else {
802        /* We're using the core specification. We can assume all of the
803         * combinations are valid, provided the OpenGL enums values are also
804         * valid for textures. */
805        TextureFormatFilter::new_permissive()
806    };
807
808    (info, features, legacy, properties, private, filter)
809}
810
811#[cfg(test)]
812mod tests {
813    use super::Version;
814
815    #[test]
816    fn test_version_parse() {
817        assert_eq!(Version::parse("1"), Err("1"));
818        assert_eq!(Version::parse("1."), Err("1."));
819        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
820        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
821        assert_eq!(
822            Version::parse("1.2.3"),
823            Ok(Version::new(1, 2, Some(3), String::new()))
824        );
825        assert_eq!(
826            Version::parse("1.2"),
827            Ok(Version::new(1, 2, None, String::new()))
828        );
829        assert_eq!(
830            Version::parse("1.2 h3l1o. W0rld"),
831            Ok(Version::new(1, 2, None, "h3l1o. W0rld".to_string()))
832        );
833        assert_eq!(
834            Version::parse("1.2.h3l1o. W0rld"),
835            Ok(Version::new(1, 2, None, "W0rld".to_string()))
836        );
837        assert_eq!(
838            Version::parse("1.2. h3l1o. W0rld"),
839            Ok(Version::new(1, 2, None, "h3l1o. W0rld".to_string()))
840        );
841        assert_eq!(
842            Version::parse("1.2.3.h3l1o. W0rld"),
843            Ok(Version::new(1, 2, Some(3), "W0rld".to_string()))
844        );
845        assert_eq!(
846            Version::parse("1.2.3 h3l1o. W0rld"),
847            Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld".to_string()))
848        );
849        assert_eq!(
850            Version::parse("OpenGL ES 3.1"),
851            Ok(Version::new_embedded(3, 1, String::new()))
852        );
853        assert_eq!(
854            Version::parse("OpenGL ES 2.0 Google Nexus"),
855            Ok(Version::new_embedded(2, 0, "Google Nexus".to_string()))
856        );
857        assert_eq!(
858            Version::parse("GLSL ES 1.1"),
859            Ok(Version::new_embedded(1, 1, String::new()))
860        );
861        assert_eq!(
862            Version::parse("OpenGL ES GLSL ES 3.20"),
863            Ok(Version::new_embedded(3, 2, String::new()))
864        );
865        assert_eq!(
866            // WebGL 2.0 should parse as OpenGL ES 3.0
867            Version::parse("WebGL 2.0 (OpenGL ES 3.0 Chromium)"),
868            Ok(Version::new_embedded(
869                3,
870                0,
871                "(OpenGL ES 3.0 Chromium)".to_string()
872            ))
873        );
874        assert_eq!(
875            Version::parse("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)"),
876            Ok(Version::new_embedded(
877                3,
878                0,
879                "(OpenGL ES GLSL ES 3.0 Chromium)".to_string()
880            ))
881        );
882    }
883}