use alloc::string::String;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicUsize, Ordering};
use regex::Regex;
use gl;
use gl::types::*;
static MAJOR: AtomicUsize = AtomicUsize::new(3);
static MINOR: AtomicUsize = AtomicUsize::new(0);
static GLSL_MAJOR: AtomicUsize = AtomicUsize::new(3);
static GLSL_MINOR: AtomicUsize = AtomicUsize::new(0);
static HIGHP: &'static str = "highp";
static MEDIUMP: &'static str = "mediump";
static LOWP: &'static str = "lowp";
#[inline(always)]
pub fn gl_major() -> usize {
MAJOR.load(Ordering::Relaxed)
}
#[inline(always)]
pub fn gl_minor() -> usize {
MINOR.load(Ordering::Relaxed)
}
#[inline(always)]
pub fn glsl_major() -> usize {
GLSL_MAJOR.load(Ordering::Relaxed)
}
#[inline(always)]
pub fn glsl_minor() -> usize {
GLSL_MINOR.load(Ordering::Relaxed)
}
pub struct GLInfo {
version: String,
major: usize,
minor: usize,
glsl_major: usize,
glsl_minor: usize,
extenstions: Vec<String>,
max_anisotropy: usize,
max_textures: usize,
max_vertex_textures: usize,
max_texture_size: usize,
max_cube_texture_size: usize,
max_render_buffer_size: usize,
max_uniforms: usize,
max_varyings: usize,
max_attributes: usize,
precision: &'static str,
}
impl GLInfo {
#[inline(always)]
pub fn new() -> Self {
let mut info = GLInfo {
version: String::new(),
major: 0,
minor: 0,
glsl_major: 0,
glsl_minor: 0,
extenstions: Vec::new(),
max_anisotropy: 0,
max_textures: 0,
max_vertex_textures: 0,
max_texture_size: 0,
max_cube_texture_size: 0,
max_render_buffer_size: 0,
max_uniforms: 0,
max_varyings: 0,
max_attributes: 0,
precision: HIGHP,
};
info.gl_info();
info
}
#[inline(always)]
pub fn version(&self) -> &String { &self.version }
#[inline(always)]
pub fn major(&self) -> usize { self.major }
#[inline(always)]
pub fn minor(&self) -> usize { self.minor }
#[inline(always)]
pub fn glsl_major(&self) -> usize { self.glsl_major }
#[inline(always)]
pub fn glsl_minor(&self) -> usize { self.glsl_minor }
#[inline(always)]
#[inline(always)]
pub fn extenstions(&self) -> &[String] { &*self.extenstions }
#[inline(always)]
pub fn max_anisotropy(&self) -> usize { self.max_anisotropy }
#[inline(always)]
pub fn max_textures(&self) -> usize { self.max_textures }
#[inline(always)]
pub fn max_vertex_textures(&self) -> usize { self.max_vertex_textures }
#[inline(always)]
pub fn max_texture_size(&self) -> usize { self.max_texture_size }
#[inline(always)]
pub fn max_cube_texture_size(&self) -> usize { self.max_cube_texture_size }
#[inline(always)]
pub fn max_render_buffer_size(&self) -> usize { self.max_render_buffer_size }
#[inline(always)]
pub fn max_uniforms(&self) -> usize { self.max_uniforms }
#[inline(always)]
pub fn max_varyings(&self) -> usize { self.max_varyings }
#[inline(always)]
pub fn max_attributes(&self) -> usize { self.max_attributes }
#[inline(always)]
pub fn precision(&self) -> &'static str { self.precision }
#[inline(always)]
pub fn has_extenstion(&self, string: &str) -> bool {
match self.extenstions.iter().position(|e| e == string) {
Some(_) => true,
None => false,
}
}
#[inline]
fn gl_info(&mut self) {
let mut vs_high_float_precision: GLint = 0;
let mut vs_high_float_range: GLint = 0;
unsafe {
gl::GetShaderPrecisionFormat(
gl::VERTEX_SHADER,
gl::HIGH_FLOAT,
&mut vs_high_float_range,
&mut vs_high_float_precision
);
}
let mut vs_mediump_float_precision: GLint = 0;
let mut vs_mediump_float_range: GLint = 0;
unsafe {
gl::GetShaderPrecisionFormat(
gl::VERTEX_SHADER,
gl::MEDIUM_FLOAT,
&mut vs_mediump_float_range,
&mut vs_mediump_float_precision
);
}
let mut fs_high_float_precision: GLint = 0;
let mut fs_high_float_range: GLint = 0;
unsafe {
gl::GetShaderPrecisionFormat(
gl::FRAGMENT_SHADER,
gl::HIGH_FLOAT,
&mut fs_high_float_range,
&mut fs_high_float_precision
);
}
let mut fs_mediump_float_precision: GLint = 0;
let mut fs_mediump_float_range: GLint = 0;
unsafe {
gl::GetShaderPrecisionFormat(
gl::FRAGMENT_SHADER,
gl::MEDIUM_FLOAT,
&mut fs_mediump_float_range,
&mut fs_mediump_float_precision
);
}
let highp_available = vs_high_float_precision > 0 && fs_high_float_precision > 0;
let mediump_available = vs_mediump_float_precision > 0 && fs_mediump_float_precision > 0;
self.precision = if !highp_available {
if mediump_available {
MEDIUMP
} else {
LOWP
}
} else {
HIGHP
};
unsafe {
let ptr = gl::GetString(gl::VERSION);
string_from_ptr(ptr, &mut self.version);
let (mut major, mut minor) = match
Regex::new(r"(\d+).(\d+)")
.expect("regex failed to compile")
.captures(&self.version) {
Some(cap) => (
match cap.get(1) {
Some(major) => major.as_str().parse::<i32>().unwrap(),
None => 3,
},
match cap.get(2) {
Some(minor) => minor.as_str().parse::<i32>().unwrap(),
None => 1,
}
),
None => (3, 1),
};
if major > 2 {
gl::GetIntegerv(gl::MAJOR_VERSION, &mut major);
self.major = major as usize;
gl::GetIntegerv(gl::MINOR_VERSION, &mut minor);
self.minor = minor as usize;
} else {
self.major = 2;
self.minor = 0;
}
glsl_version(self.major, self.minor, &mut self.glsl_major, &mut self.glsl_minor);
parse_extenstions(&mut self.extenstions, self.major);
}
MAJOR.store(self.major, Ordering::SeqCst);
MINOR.store(self.minor, Ordering::SeqCst);
GLSL_MAJOR.store(self.glsl_major, Ordering::SeqCst);
GLSL_MINOR.store(self.glsl_minor, Ordering::SeqCst);
unsafe {
let mut max_textures = 0;
gl::GetIntegerv(gl::MAX_TEXTURE_IMAGE_UNITS, &mut max_textures);
self.max_textures = max_textures as usize;
let mut max_vertex_textures = 0;
gl::GetIntegerv(gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mut max_vertex_textures);
self.max_vertex_textures = max_vertex_textures as usize;
let mut max_texture_size = 0;
gl::GetIntegerv(gl::MAX_TEXTURE_SIZE, &mut max_texture_size);
self.max_texture_size = max_texture_size as usize;
let mut max_cube_texture_size = 0;
gl::GetIntegerv(gl::MAX_CUBE_MAP_TEXTURE_SIZE, &mut max_cube_texture_size);
self.max_cube_texture_size = max_cube_texture_size as usize;
let mut max_render_buffer_size = 0;
gl::GetIntegerv(gl::MAX_RENDERBUFFER_SIZE, &mut max_render_buffer_size);
self.max_render_buffer_size = max_render_buffer_size as usize;
let mut vs_max_uniforms = 0;
let mut fs_max_uniforms = 0;
gl::GetIntegerv(gl::MAX_VERTEX_UNIFORM_VECTORS, &mut vs_max_uniforms);
gl::GetIntegerv(gl::MAX_FRAGMENT_UNIFORM_VECTORS, &mut fs_max_uniforms);
self.max_uniforms = if vs_max_uniforms < fs_max_uniforms {
vs_max_uniforms
} else {
fs_max_uniforms
} as usize * 4;
let mut max_varyings = 0;
gl::GetIntegerv(gl::MAX_VARYING_VECTORS, &mut max_varyings);
self.max_varyings = max_varyings as usize * 4;
let mut max_attributes = 0;
gl::GetIntegerv(gl::MAX_VERTEX_ATTRIBS, &mut max_attributes);
self.max_attributes = max_attributes as usize;
}
}
}
#[inline]
unsafe fn string_from_ptr(ptr: *const u8, string: &mut String) {
let mut i = 0isize;
loop {
let ch = *ptr.offset(i);
if ch != 0u8 {
string.push(ch as char);
i = i + 1isize;
} else {
break;
}
}
}
#[inline]
unsafe fn parse_extenstions(extenstions: &mut Vec<String>, major_version: usize) {
if major_version > 2 {
let mut count = 0;
gl::GetIntegerv(gl::NUM_EXTENSIONS, &mut count);
for i in 0..(count as u32) {
let mut string = String::new();
string_from_ptr(gl::GetStringi(gl::EXTENSIONS, i), &mut string);
extenstions.push(string);
}
} else {
let mut string = String::new();
string_from_ptr(gl::GetString(gl::EXTENSIONS), &mut string);
for extenstion in string.split_whitespace() {
extenstions.push(String::from(extenstion));
}
}
}
#[inline]
fn glsl_version(major: usize, minor: usize, glsl_major: &mut usize, glsl_minor: &mut usize) {
if major <= 3 && minor <= 2 {
*glsl_major = 1;
*glsl_minor = if major == 3 && minor == 2 {
5
} else if major == 3 && minor == 1 {
4
} else if major == 3 && minor == 0 {
3
} else if major == 2 && minor == 1 {
2
} else {
1
}
} else {
*glsl_major = major;
*glsl_minor = minor;
}
}