#![feature(core_intrinsics)]
#![feature(untagged_unions)]
#![feature(concat_idents)]
#![feature(allocator_api)]
#![feature(trace_macros)]
#![feature(unsize)]
#![feature(coerce_unsized)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_uninit_array)]
#![allow(incomplete_features)]
#![feature(specialization)]
#![recursion_limit="8192"]
pub extern crate gl;
use gl::types::*;
use std::convert::TryFrom;
use std::fmt;
use std::fmt::{Display, Debug, Formatter};
use std::hash::Hash;
pub use program::*;
pub use glsl::*;
pub use buffer::*;
macro_rules! display_from_debug {
($name:ty) => {
impl ::std::fmt::Display for $name {
#[inline]
fn fmt(&self,f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::std::fmt::Debug::fmt(self, f)
}
}
}
}
macro_rules! glenum {
({$($kw:tt)*} enum $name:ident {$($(#[$attr:meta])* $item:ident),*} $($tt:tt)*) => {
glenum!({#[allow(non_camel_case_types)] $($kw)*} enum $name {$($(#[$attr])* [$item $item stringify!($item)]),*} $($tt)*);
};
({$($kw:tt)*} enum $name:ident {$($(#[$attr:meta])* [$item:ident $gl:ident $pretty:expr] ),*} $($tt:tt)*) => {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
$($kw)* enum $name {
$(
$(#[$attr])*
$item = ::gl::$gl as isize
),*
}
impl ::std::fmt::Display for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
$($name::$item => write!(f, $pretty)),*
}
}
}
impl From<$name> for gl::types::GLenum {
fn from(e: $name) -> ::gl::types::GLenum {
match e {
$($name::$item => ::gl::$gl),*
}
}
}
impl ::std::convert::TryFrom<::gl::types::GLenum> for $name {
type Error = GLError;
fn try_from(e: ::gl::types::GLenum) -> Result<$name, GLError>{
match e {
$(::gl::$gl => Ok($name::$item),)*
_ => Err(crate::GLError::InvalidEnum(e, stringify!($name).to_string()))
}
}
}
impl $crate::GLEnum for $name {}
glenum!($($tt)*);
};
({$($kws:tt)*} #[$attr:meta] $($tt:tt)*) => { glenum!({$($kws)* #[$attr]} $($tt)*); };
({$($kws:tt)*} $kw:ident($($path:tt)*) $($tt:tt)*) => { glenum!({$($kws)* $kw($($path)*)} $($tt)*); };
({$($kws:tt)*} $kw:ident $($tt:tt)*) => { glenum!({$($kws)* $kw} $($tt)*); };
() => {};
(# $($tt:tt)*) => {glenum!({} # $($tt)*);};
($kw:ident $($tt:tt)*) => {glenum!({} $kw $($tt)*);};
}
macro_rules! impl_tuple {
($callback:ident) => {impl_tuple!({A:a B:b C:c D:d E:e F:f G:g H:h I:i K:k J:j} L:l $callback);};
($callback:ident @with_last) => {
impl_tuple!({A:a B:b C:c D:d E:e F:f G:g H:h I:i K:k J:j} L:l $callback @with_last);
};
({} $callback:ident) => {};
({} $T0:ident:$t0:ident $callback:ident ) => {};
({} $T0:ident:$t0:ident $callback:ident @$($options:tt)*) => {};
({$($A:ident:$a:ident)*} $T0:ident:$t0:ident $callback:ident) => {
$callback!($($A:$a)* $T0:$t0);
impl_tuple!({} $($A:$a)* $callback);
};
({$($A:ident:$a:ident)*} $T0:ident:$t0:ident $callback:ident @with_last) => {
$callback!({$($A:$a)*} $T0:$t0);
impl_tuple!({} $($A:$a)* $callback @with_last);
};
({$($A:ident:$a:ident)*} $T0:ident:$t0:ident $T1:ident:$t1:ident $($rest:tt)*) => {
impl_tuple!({$($A:$a)* $T0:$t0} $T1:$t1 $($rest)*);
};
}
macro_rules! check_loaded {
($gl_fun0:ident, $($gl_fun:ident),+; $expr:expr) => {
check_loaded!($gl_fun0; check_loaded!($($gl_fun),+; $expr)).map_or_else(|e| Err(e), |ok| ok)
};
($gl_fun:ident; $expr:expr) => {
if $crate::gl::$gl_fun::is_loaded() {
Ok($expr)
} else {
Err($crate::GLError::FunctionNotLoaded(concat!("gl", stringify!($gl_fun))))
}
}
}
#[macro_use]
pub mod glsl;
pub mod program;
pub mod buffer;
pub trait Surface: {
fn is_active(&self) -> bool;
fn make_current(&mut self) -> &mut Context;
}
pub struct GLProvider { _private: () }
pub struct GL2 { _private: () }
pub struct GL3 { _private: () }
pub struct GL4 { _private: () }
impl GLProvider {
pub fn get_current() -> Result<GLProvider, ()> {
if gl::Finish::is_loaded() {
Ok(GLProvider{ _private: () })
} else {
Err(())
}
}
pub unsafe fn load<F: FnMut(&'static str) -> *const GLvoid>(proc_addr: F) -> GLProvider {
gl::load_with(proc_addr);
GLProvider{ _private: () }
}
#[inline] pub fn upgrade(&self) -> Result<&GL2, GLError> {
check_loaded!(
GenBuffers, BindBuffer, DeleteBuffers, GetBufferParameteriv,
BufferData, BufferSubData, GetBufferSubData, CopyBufferSubData,
MapBuffer, UnmapBuffer;
&GL2{_private:()}
)
}
}
impl GL2 {
#[inline] pub fn upgrade(&self) -> Result<&GL3, GLError> {
check_loaded!(MapBuffer, UnmapBuffer; &GL3{_private:()} )
}
}
impl GL3 {
#[inline] pub fn as_gl2(&self) -> &GL2 {&GL2{_private:()}}
#[inline] pub fn upgrade(&self) -> Result<&GL4, GLError> {
check_loaded!(BufferStorage, MapBufferRange; &GL4{_private:()} )
}
}
impl GL4 {
#[inline] pub fn as_gl2(&self) -> &GL2 {&GL2{_private:()}}
#[inline] pub fn as_gl3(&self) -> &GL3 {&GL3{_private:()}}
}
pub unsafe trait Resource:Sized {
type GL;
type BindingTarget: Target<Resource=Self>;
fn id(&self) -> GLuint;
fn into_raw(self) -> GLuint;
unsafe fn from_raw(id:GLuint) -> Option<Self>;
fn gen(gl: &Self::GL) -> Self;
fn gen_resources(gl: &Self::GL, count: GLuint) -> Box<[Self]>;
fn is(id: GLuint) -> bool;
#[inline] fn obj_eq<R:Resource+?Sized>(&self, rhs:&R) -> bool {self.id()==rhs.id()}
fn delete(self);
fn delete_resources(resouces: Box<[Self]>);
}
pub unsafe trait Target: GLEnum {
type Resource: Resource<BindingTarget=Self>;
unsafe fn bind(self, id: GLuint);
#[inline]
unsafe fn as_loc(self) -> BindingLocation<Self::Resource> {
BindingLocation(self)
}
}
#[derive(PartialEq, Eq, Hash)]
pub struct BindingLocation<R:Resource>(R::BindingTarget);
pub struct Binding<'a,R:Resource>(&'a BindingLocation<R>, GLuint);
impl<'a,R:Resource> Binding<'a,R> {
#[inline] pub fn target(&self) -> R::BindingTarget { self.0.target() }
#[inline] pub fn target_id(&self) -> GLenum { self.0.target_id() }
#[inline] pub fn resource_id(&self) -> GLuint { self.1 }
}
impl<'a,R:Resource> Drop for Binding<'a,R> {
#[inline] fn drop(&mut self) { unsafe { self.target().bind(0) } }
}
impl<R:Resource> BindingLocation<R> {
pub fn target(&self) -> R::BindingTarget { self.0 }
pub fn target_id(&self) -> GLenum { self.0.into() }
#[inline]
pub unsafe fn new(target: R::BindingTarget) -> Self {BindingLocation(target)}
#[inline]
pub fn bind_raw<'a>(&'a mut self, id: GLuint) -> Result<Binding<'a,R>, GLError> {
if R::is(id) {
unsafe { self.target().bind(id); }
Ok(Binding(self, id))
} else {
Err(GLError::InvalidOperation("Cannot bind resource to the given target".to_string()))
}
}
#[inline]
pub fn bind<'a>(&'a mut self, resource: &'a R) -> Binding<'a,R> {
unsafe { self.target().bind(resource.id()); }
Binding(self, resource.id())
}
}
pub struct Context {
_private: ::std::marker::PhantomData<*const ()>
}
impl Context {
pub fn init(_gl: &GLProvider) -> Context {
Context { _private: ::std::marker::PhantomData }
}
}
glenum! {
pub enum IntType {
[Byte BYTE "Byte"],
[UByte UNSIGNED_BYTE "UByte"],
[Short SHORT "Short"],
[UShort UNSIGNED_SHORT "UShort"],
[Int INT "Int"],
[UInt UNSIGNED_INT "UInt"]
}
pub enum FloatType {
[Half HALF_FLOAT "Half"],
[Float FLOAT "FLoat"]
}
}
impl IntType {
#[inline]
pub fn size_of(self) -> usize {
match self {
IntType::Byte | IntType::UByte => 1,
IntType::Short |IntType::UShort => 2,
IntType::Int | IntType::UInt => 4
}
}
}
impl FloatType {
#[inline]
pub fn size_of(self) -> usize {
match self {
FloatType::Half => 2,
FloatType::Float => 4,
}
}
}
pub trait GLEnum: Sized + Copy + Eq + Hash + Debug + Display + Into<GLenum> + TryFrom<GLenum, Error=GLError> {}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum GLError {
ShaderCompilation(GLenum, ShaderType, String),
ProgramLinking(GLenum, String),
ProgramValidation(GLenum, String),
InvalidEnum(GLenum, String),
InvalidOperation(String),
InvalidBits(GLbitfield, String),
BufferCopySizeError(usize, usize),
FunctionNotLoaded(&'static str)
}
display_from_debug!(GLError);
impl Debug for GLError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
GLError::ShaderCompilation(id, ty, log) => write!(f, "{} #{} compilation error: {}", ty, id, log),
GLError::ProgramLinking(id, log) => write!(f, "Program #{} link error with Program: {}", id, log),
GLError::ProgramValidation(id, log) => write!(f, "Program #{} validation error: {}", id, log),
GLError::InvalidEnum(id, ty) => write!(f, "Invalid enum: #{} is not a valid {}", id, ty),
GLError::InvalidOperation(msg) => write!(f, "Invalid operation: {}", msg),
GLError::InvalidBits(id, ty) => write!(f, "Invalid bitfield: {:b} are not valid flags for {}", id, ty),
GLError::FunctionNotLoaded(name) => write!(f, "{} not loaded", name),
GLError::BufferCopySizeError(s, cap) =>
write!(f, "Invalid Buffer Copy: Source size {} smaller than Destination capacity {}.
(If you are using an array, try slicing first.)", s, cap),
}
}
}
pub trait Boolean {
type Not: Boolean<Not=Self>;
const VALUE: bool;
}
pub struct True;
pub struct False;
impl Boolean for True {
type Not = False;
const VALUE: bool = true;
}
impl Boolean for False {
type Not = True;
const VALUE: bool = false;
}