glium/
version.rs

1use std::cmp::Ordering;
2use std::ffi::CStr;
3use crate::gl;
4
5/// Describes a version.
6///
7/// A version can only be compared to another version if they belong to the same API.
8/// For example, both `Version(Gl, 3, 0) >= Version(GlEs, 3, 0)` and `Version(GlEs, 3, 0) >=
9/// Version(Gl, 3, 0)` return `false`.
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct Version(pub Api, pub u8, pub u8);
12
13/// Describes an OpenGL-related API.
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub enum Api {
16    /// Regular OpenGL.
17    Gl,
18    /// OpenGL embedded system.
19    GlEs,
20}
21
22impl PartialOrd for Version {
23    #[inline]
24    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
25        if self.0 != other.0 {
26            return None;
27        }
28
29        match self.1.cmp(&other.1) {
30            Ordering::Equal => Some(self.2.cmp(&other.2)),
31            a => Some(a)
32        }
33    }
34}
35
36/// Obtains the OpenGL version of the current context using the loaded functions.
37///
38/// # Unsafe
39///
40/// You must ensure that the functions belong to the current context, otherwise you will get
41/// an undefined behavior.
42pub unsafe fn get_gl_version(gl: &gl::Gl) -> Version {
43    let version = gl.GetString(gl::VERSION);
44    let version = String::from_utf8(CStr::from_ptr(version as *const _).to_bytes().to_vec()).unwrap();
45
46    // for the moment we mock WebGL as OpenGL ES 2.0
47    // TODO: handle the differences between WebGL and OpenGL ES
48    if version.starts_with("WebGL ") {
49        return Version(Api::GlEs, 2, 0);
50    }
51
52    let (version, api) = if version.starts_with("OpenGL ES ") {
53        (&version[10..], Api::GlEs)
54    } else if version.starts_with("OpenGL ES-") {
55        (&version[13..], Api::GlEs)
56    } else {
57        (&version[..], Api::Gl)
58    };
59
60    let version = version.split(' ').next().expect("glGetString(GL_VERSION) returned an empty \
61                                                    string");
62
63    let mut iter = version.split('.');
64    let major = iter.next().unwrap();
65    let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");
66
67    Version(
68        api,
69        major.parse().ok().expect("failed to parse GL major version"),
70        minor.parse().ok().expect("failed to parse GL minor version"),
71    )
72}
73
74/// Given an API version, this function returns the GLSL version that the implementation is
75/// required to support.
76///
77/// # Panic
78///
79/// Panics if the version is invalid or is not supposed to support a GLSL version.
80pub fn get_supported_glsl_version(gl_version: &Version) -> Version {
81    match gl_version.0 {
82        Api::Gl => {
83            // since OpenGL 3.3: glsl versions match gl version, just return a copy
84            if *gl_version >= Version(gl_version.0, 3, 3) {
85                return *gl_version;
86            }
87
88            // match to detect invalid versions
89            match *gl_version {
90                Version(a, 2, 0) => Version(a, 1, 1),
91                Version(a, 2, 1) => Version(a, 1, 2),
92                Version(a, 3, 0) => Version(a, 1, 3),
93                Version(a, 3, 1) => Version(a, 1, 4),
94                Version(a, 3, 2) => Version(a, 1, 5),
95                _ => panic!("no corresponding glsl version exists")
96            }
97        },
98        Api::GlEs => {
99            // since OpenGL ES 3.0: glsl versions match gl version, just return a copy
100            if *gl_version >= Version(gl_version.0, 3, 0) {
101                return *gl_version;
102            }
103
104            // only other valid GLES version is 2.0
105            if *gl_version == Version(gl_version.0, 2, 0){
106                Version(Api::GlEs, 1, 0)
107            } else {
108                panic!("no corresponding glsl version exists")
109            }
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::{Version, Api, get_supported_glsl_version};
117
118    macro_rules! assert_versions {
119        ( $api:path, $gl_major:expr, $gl_minor:expr => $glsl_major:expr, $glsl_minor:expr) => {
120            assert_eq!(
121                get_supported_glsl_version(&Version($api, $gl_major, $gl_minor)),
122                Version($api, $glsl_major, $glsl_minor)
123                    );
124        }
125    }
126
127    #[test]
128    fn valid_gl_versions() {
129        // irregular versions
130        assert_versions!(Api::Gl, 2, 0 => 1, 1);
131        assert_versions!(Api::Gl, 2, 1 => 1, 2);
132        assert_versions!(Api::Gl, 3, 0 => 1, 3);
133        assert_versions!(Api::Gl, 3, 1 => 1, 4);
134        assert_versions!(Api::Gl, 3, 2 => 1, 5);
135
136        // test a few regular versions
137        assert_versions!(Api::Gl, 3, 3 => 3, 3);
138        assert_versions!(Api::Gl, 4, 0 => 4, 0);
139        assert_versions!(Api::Gl, 4, 5 => 4, 5);
140    }
141
142    #[test]
143    fn valid_gles_versions() {
144        // only irregular version
145        assert_versions!(Api::GlEs, 2, 0 => 1, 0);
146
147        // some regular versions
148        assert_versions!(Api::GlEs, 3, 0 => 3, 0);
149        assert_versions!(Api::GlEs, 3, 1 => 3, 1);
150    }
151
152    #[test]
153    #[should_panic]
154    fn invalid_gl_version() {
155        get_supported_glsl_version(&Version(Api::Gl, 1, 0));
156    }
157
158    #[test]
159    #[should_panic]
160    fn invalid_gles_version() {
161        get_supported_glsl_version(&Version(Api::GlEs, 1, 0));
162    }
163}