pub mod types;
use crate::{
backend::shader::{Shader, ShaderData as ShaderDataBackend, Uniformable},
context::GraphicsContext,
vertex::Semantics,
};
use std::{error, fmt, marker::PhantomData};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum StageType {
VertexShader,
TessellationControlShader,
TessellationEvaluationShader,
GeometryShader,
FragmentShader,
}
impl fmt::Display for StageType {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
StageType::VertexShader => f.write_str("vertex shader"),
StageType::TessellationControlShader => f.write_str("tessellation control shader"),
StageType::TessellationEvaluationShader => f.write_str("tessellation evaluation shader"),
StageType::GeometryShader => f.write_str("geometry shader"),
StageType::FragmentShader => f.write_str("fragment shader"),
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StageError {
CompilationFailed(StageType, String),
UnsupportedType(StageType),
}
impl StageError {
pub fn compilation_failed(ty: StageType, reason: impl Into<String>) -> Self {
StageError::CompilationFailed(ty, reason.into())
}
pub fn unsupported_type(ty: StageType) -> Self {
StageError::UnsupportedType(ty)
}
}
impl fmt::Display for StageError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
StageError::CompilationFailed(ref ty, ref r) => write!(f, "{} compilation error: {}", ty, r),
StageError::UnsupportedType(ty) => write!(f, "unsupported {}", ty),
}
}
}
impl error::Error for StageError {}
impl From<StageError> for ProgramError {
fn from(e: StageError) -> Self {
ProgramError::StageError(e)
}
}
pub struct TessellationStages<'a, S>
where
S: ?Sized,
{
pub control: &'a S,
pub evaluation: &'a S,
}
#[non_exhaustive]
#[derive(Debug, Eq, PartialEq)]
pub enum ProgramError {
CreationFailed(String),
StageError(StageError),
LinkFailed(String),
Warning(ProgramWarning),
}
impl ProgramError {
pub fn creation_failed(reason: impl Into<String>) -> Self {
ProgramError::CreationFailed(reason.into())
}
pub fn stage_error(e: StageError) -> Self {
ProgramError::StageError(e)
}
pub fn link_failed(reason: impl Into<String>) -> Self {
ProgramError::LinkFailed(reason.into())
}
pub fn warning(w: ProgramWarning) -> Self {
ProgramError::Warning(w)
}
}
impl fmt::Display for ProgramError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
ProgramError::CreationFailed(ref e) => write!(f, "cannot create shader program: {}", e),
ProgramError::StageError(ref e) => write!(f, "shader program has stage error: {}", e),
ProgramError::LinkFailed(ref s) => write!(f, "shader program failed to link: {}", s),
ProgramError::Warning(ref e) => write!(f, "shader program warning: {}", e),
}
}
}
impl error::Error for ProgramError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
ProgramError::StageError(e) => Some(e),
_ => None,
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum ProgramWarning {
Uniform(UniformWarning),
VertexAttrib(VertexAttribWarning),
}
impl fmt::Display for ProgramWarning {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
ProgramWarning::Uniform(ref e) => write!(f, "uniform warning: {}", e),
ProgramWarning::VertexAttrib(ref e) => write!(f, "vertex attribute warning: {}", e),
}
}
}
impl error::Error for ProgramWarning {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
ProgramWarning::Uniform(e) => Some(e),
ProgramWarning::VertexAttrib(e) => Some(e),
}
}
}
impl From<ProgramWarning> for ProgramError {
fn from(e: ProgramWarning) -> Self {
ProgramError::Warning(e)
}
}
#[non_exhaustive]
#[derive(Debug, Eq, PartialEq)]
pub enum UniformWarning {
Inactive(String),
TypeMismatch(String, UniformType),
UnsupportedType(String, UniformType),
SizeMismatch {
name: String,
size: usize,
found_size: usize,
},
}
impl UniformWarning {
pub fn inactive<N>(name: N) -> Self
where
N: Into<String>,
{
UniformWarning::Inactive(name.into())
}
pub fn type_mismatch<N>(name: N, ty: UniformType) -> Self
where
N: Into<String>,
{
UniformWarning::TypeMismatch(name.into(), ty)
}
pub fn unsupported_type<N>(name: N, ty: UniformType) -> Self
where
N: Into<String>,
{
UniformWarning::UnsupportedType(name.into(), ty)
}
pub fn size_mismatch(name: impl Into<String>, size: usize, found_size: usize) -> Self {
UniformWarning::SizeMismatch {
name: name.into(),
size,
found_size,
}
}
}
impl fmt::Display for UniformWarning {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
UniformWarning::Inactive(ref s) => write!(f, "inactive {} uniform", s),
UniformWarning::TypeMismatch(ref n, ref t) => {
write!(f, "type mismatch for uniform {}: {}", n, t)
}
UniformWarning::UnsupportedType(ref name, ref ty) => {
write!(f, "unsupported type {} for uniform {}", ty, name)
}
UniformWarning::SizeMismatch {
ref name,
size,
found_size,
} => {
write!(
f,
"size mismatch for uniform {}: {} (tdetected size={}",
name, size, found_size
)
}
}
}
}
impl From<UniformWarning> for ProgramWarning {
fn from(e: UniformWarning) -> Self {
ProgramWarning::Uniform(e)
}
}
impl error::Error for UniformWarning {}
#[non_exhaustive]
#[derive(Debug, Eq, PartialEq)]
pub enum VertexAttribWarning {
Inactive(String),
}
impl VertexAttribWarning {
pub fn inactive(attrib: impl Into<String>) -> Self {
VertexAttribWarning::Inactive(attrib.into())
}
}
impl fmt::Display for VertexAttribWarning {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
VertexAttribWarning::Inactive(ref s) => write!(f, "inactive {} vertex attribute", s),
}
}
}
impl From<VertexAttribWarning> for ProgramWarning {
fn from(e: VertexAttribWarning) -> Self {
ProgramWarning::VertexAttrib(e)
}
}
impl error::Error for VertexAttribWarning {}
#[derive(Debug)]
pub struct Uniform<T>
where
T: ?Sized,
{
index: i32,
_t: PhantomData<*const T>,
}
impl<T> Uniform<T>
where
T: ?Sized,
{
pub unsafe fn new(index: i32) -> Self {
Uniform {
index,
_t: PhantomData,
}
}
pub fn index(&self) -> i32 {
self.index
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UniformType {
Int,
UInt,
Float,
Double,
Bool,
IVec2,
IVec3,
IVec4,
UIVec2,
UIVec3,
UIVec4,
Vec2,
Vec3,
Vec4,
DVec2,
DVec3,
DVec4,
BVec2,
BVec3,
BVec4,
M22,
M33,
M44,
DM22,
DM33,
DM44,
ISampler1D,
ISampler2D,
ISampler3D,
ISampler1DArray,
ISampler2DArray,
UISampler1D,
UISampler2D,
UISampler3D,
UISampler1DArray,
UISampler2DArray,
Sampler1D,
Sampler2D,
Sampler3D,
Sampler1DArray,
Sampler2DArray,
ICubemap,
UICubemap,
Cubemap,
ShaderDataBinding,
}
impl fmt::Display for UniformType {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
UniformType::Int => f.write_str("int"),
UniformType::UInt => f.write_str("uint"),
UniformType::Float => f.write_str("float"),
UniformType::Double => f.write_str("double"),
UniformType::Bool => f.write_str("bool"),
UniformType::IVec2 => f.write_str("ivec2"),
UniformType::IVec3 => f.write_str("ivec3"),
UniformType::IVec4 => f.write_str("ivec4"),
UniformType::UIVec2 => f.write_str("uvec2"),
UniformType::UIVec3 => f.write_str("uvec3"),
UniformType::UIVec4 => f.write_str("uvec4"),
UniformType::Vec2 => f.write_str("vec2"),
UniformType::Vec3 => f.write_str("vec3"),
UniformType::Vec4 => f.write_str("vec4"),
UniformType::DVec2 => f.write_str("dvec2"),
UniformType::DVec3 => f.write_str("dvec3"),
UniformType::DVec4 => f.write_str("dvec4"),
UniformType::BVec2 => f.write_str("bvec2"),
UniformType::BVec3 => f.write_str("bvec3"),
UniformType::BVec4 => f.write_str("bvec4"),
UniformType::M22 => f.write_str("mat2"),
UniformType::M33 => f.write_str("mat3"),
UniformType::M44 => f.write_str("mat4"),
UniformType::DM22 => f.write_str("dmat2"),
UniformType::DM33 => f.write_str("dmat3"),
UniformType::DM44 => f.write_str("dmat4"),
UniformType::ISampler1D => f.write_str("isampler1D"),
UniformType::ISampler2D => f.write_str("isampler2D"),
UniformType::ISampler3D => f.write_str("isampler3D"),
UniformType::ISampler1DArray => f.write_str("isampler1DArray"),
UniformType::ISampler2DArray => f.write_str("isampler2DArray"),
UniformType::UISampler1D => f.write_str("usampler1D"),
UniformType::UISampler2D => f.write_str("usampler2D"),
UniformType::UISampler3D => f.write_str("usampler3D"),
UniformType::UISampler1DArray => f.write_str("usampler1DArray"),
UniformType::UISampler2DArray => f.write_str("usampler2DArray"),
UniformType::Sampler1D => f.write_str("sampler1D"),
UniformType::Sampler2D => f.write_str("sampler2D"),
UniformType::Sampler3D => f.write_str("sampler3D"),
UniformType::Sampler1DArray => f.write_str("sampler1DArray"),
UniformType::Sampler2DArray => f.write_str("sampler2DArray"),
UniformType::ICubemap => f.write_str("isamplerCube"),
UniformType::UICubemap => f.write_str("usamplerCube"),
UniformType::Cubemap => f.write_str("samplerCube"),
UniformType::ShaderDataBinding => f.write_str("shader data binding"),
}
}
}
pub struct Stage<B>
where
B: ?Sized + Shader,
{
repr: B::StageRepr,
}
impl<B> Stage<B>
where
B: ?Sized + Shader,
{
pub fn new<C, R>(ctx: &mut C, ty: StageType, src: R) -> Result<Self, StageError>
where
C: GraphicsContext<Backend = B>,
R: AsRef<str>,
{
unsafe {
ctx
.backend()
.new_stage(ty, src.as_ref())
.map(|repr| Stage { repr })
}
}
}
pub struct UniformBuilder<'a, B>
where
B: ?Sized + Shader,
{
repr: B::UniformBuilderRepr,
warnings: Vec<UniformWarning>,
_a: PhantomData<&'a mut ()>,
}
impl<'a, B> UniformBuilder<'a, B>
where
B: ?Sized + Shader,
{
pub fn ask<T>(&mut self, name: &str) -> Result<Uniform<T>, UniformWarning>
where
B: for<'u> Uniformable<'u, T>,
{
unsafe { B::ask_uniform(&mut self.repr, name) }
}
pub fn ask_or_unbound<T>(&mut self, name: &str) -> Uniform<T>
where
B: for<'u> Uniformable<'u, T>,
{
match self.ask(name) {
Ok(uniform) => uniform,
Err(err) => {
self.warnings.push(err);
unsafe { B::unbound(&mut self.repr) }
}
}
}
}
pub trait UniformInterface<B, E = ()>: Sized
where
B: Shader,
{
fn uniform_interface<'a>(
builder: &mut UniformBuilder<'a, B>,
env: &mut E,
) -> Result<Self, UniformWarning>;
}
impl<B, E> UniformInterface<B, E> for ()
where
B: Shader,
{
fn uniform_interface<'a>(
_: &mut UniformBuilder<'a, B>,
_: &mut E,
) -> Result<Self, UniformWarning> {
Ok(())
}
}
pub struct BuiltProgram<B, Sem, Out, Uni>
where
B: Shader,
{
pub program: Program<B, Sem, Out, Uni>,
pub warnings: Vec<ProgramError>,
}
impl<B, Sem, Out, Uni> BuiltProgram<B, Sem, Out, Uni>
where
B: Shader,
{
pub fn ignore_warnings(self) -> Program<B, Sem, Out, Uni> {
self.program
}
}
pub struct AdaptationFailure<B, Sem, Out, Uni>
where
B: Shader,
{
pub program: Program<B, Sem, Out, Uni>,
pub error: ProgramError,
}
impl<B, Sem, Out, Uni> AdaptationFailure<B, Sem, Out, Uni>
where
B: Shader,
{
pub(crate) fn new(program: Program<B, Sem, Out, Uni>, error: ProgramError) -> Self {
AdaptationFailure { program, error }
}
pub fn ignore_error(self) -> Program<B, Sem, Out, Uni> {
self.program
}
}
pub struct ProgramInterface<'a, B>
where
B: Shader,
{
pub(crate) program: &'a mut B::ProgramRepr,
}
impl<'a, B> ProgramInterface<'a, B>
where
B: Shader,
{
pub fn set<'u, T>(&'u mut self, uniform: &'u Uniform<T>, value: B::Target)
where
B: Uniformable<'u, T>,
{
unsafe { B::update(self.program, uniform, value) };
}
pub fn query(&mut self) -> Result<UniformBuilder<'a, B>, ProgramError> {
unsafe {
B::new_uniform_builder(&mut self.program).map(|repr| UniformBuilder {
repr,
warnings: Vec::new(),
_a: PhantomData,
})
}
}
}
pub struct ProgramBuilder<'a, C, Sem, Out, Uni> {
ctx: &'a mut C,
_phantom: PhantomData<(Sem, Out, Uni)>,
}
impl<'a, C, Sem, Out, Uni> ProgramBuilder<'a, C, Sem, Out, Uni>
where
C: GraphicsContext,
C::Backend: Shader,
Sem: Semantics,
{
pub fn new(ctx: &'a mut C) -> Self {
ProgramBuilder {
ctx,
_phantom: PhantomData,
}
}
pub fn from_stages_env<'b, T, G, E>(
&mut self,
vertex: &'b Stage<C::Backend>,
tess: T,
geometry: G,
fragment: &'b Stage<C::Backend>,
env: &mut E,
) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
where
Uni: UniformInterface<C::Backend, E>,
T: Into<Option<TessellationStages<'b, Stage<C::Backend>>>>,
G: Into<Option<&'b Stage<C::Backend>>>,
{
let tess = tess.into();
let geometry = geometry.into();
unsafe {
let mut repr = self.ctx.backend().new_program(
&vertex.repr,
tess.map(|stages| TessellationStages {
control: &stages.control.repr,
evaluation: &stages.evaluation.repr,
}),
geometry.map(|stage| &stage.repr),
&fragment.repr,
)?;
let warnings = C::Backend::apply_semantics::<Sem>(&mut repr)?
.into_iter()
.map(|w| ProgramError::Warning(w.into()))
.collect();
let mut uniform_builder =
C::Backend::new_uniform_builder(&mut repr).map(|repr| UniformBuilder {
repr,
warnings: Vec::new(),
_a: PhantomData,
})?;
let uni =
Uni::uniform_interface(&mut uniform_builder, env).map_err(ProgramWarning::Uniform)?;
let program = Program {
repr,
uni,
_sem: PhantomData,
_out: PhantomData,
};
Ok(BuiltProgram { program, warnings })
}
}
pub fn from_stages<'b, T, G>(
&mut self,
vertex: &'b Stage<C::Backend>,
tess: T,
geometry: G,
fragment: &'b Stage<C::Backend>,
) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
where
Uni: UniformInterface<C::Backend>,
T: Into<Option<TessellationStages<'b, Stage<C::Backend>>>>,
G: Into<Option<&'b Stage<C::Backend>>>,
{
Self::from_stages_env(self, vertex, tess, geometry, fragment, &mut ())
}
pub fn from_strings_env<'b, T, G, E>(
&mut self,
vertex: &'b str,
tess: T,
geometry: G,
fragment: &'b str,
env: &mut E,
) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
where
Uni: UniformInterface<C::Backend, E>,
T: Into<Option<TessellationStages<'b, str>>>,
G: Into<Option<&'b str>>,
{
let vs_stage = Stage::new(self.ctx, StageType::VertexShader, vertex)?;
let tess_stages = match tess.into() {
Some(TessellationStages {
control,
evaluation,
}) => {
let control_stage = Stage::new(self.ctx, StageType::TessellationControlShader, control)?;
let evaluation_stage = Stage::new(
self.ctx,
StageType::TessellationEvaluationShader,
evaluation,
)?;
Some((control_stage, evaluation_stage))
}
None => None,
};
let tess_stages =
tess_stages
.as_ref()
.map(|(ref control, ref evaluation)| TessellationStages {
control,
evaluation,
});
let gs_stage = match geometry.into() {
Some(geometry) => Some(Stage::new(self.ctx, StageType::GeometryShader, geometry)?),
None => None,
};
let fs_stage = Stage::new(self.ctx, StageType::FragmentShader, fragment)?;
Self::from_stages_env(
self,
&vs_stage,
tess_stages,
gs_stage.as_ref(),
&fs_stage,
env,
)
}
pub fn from_strings<'b, T, G>(
&mut self,
vertex: &'b str,
tess: T,
geometry: G,
fragment: &'b str,
) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
where
Uni: UniformInterface<C::Backend>,
T: Into<Option<TessellationStages<'b, str>>>,
G: Into<Option<&'b str>>,
{
Self::from_strings_env(self, vertex, tess, geometry, fragment, &mut ())
}
}
pub struct Program<B, Sem, Out, Uni>
where
B: Shader,
{
pub(crate) repr: B::ProgramRepr,
pub(crate) uni: Uni,
_sem: PhantomData<*const Sem>,
_out: PhantomData<*const Out>,
}
impl<B, Sem, Out, Uni> Program<B, Sem, Out, Uni>
where
B: Shader,
Sem: Semantics,
{
pub fn adapt<Q>(self) -> Result<BuiltProgram<B, Sem, Out, Q>, AdaptationFailure<B, Sem, Out, Uni>>
where
Q: UniformInterface<B>,
{
self.adapt_env(&mut ())
}
pub fn adapt_env<Q, E>(
mut self,
env: &mut E,
) -> Result<BuiltProgram<B, Sem, Out, Q>, AdaptationFailure<B, Sem, Out, Uni>>
where
Q: UniformInterface<B, E>,
{
let mut uniform_builder: UniformBuilder<B> =
match unsafe { B::new_uniform_builder(&mut self.repr) } {
Ok(repr) => UniformBuilder {
repr,
warnings: Vec::new(),
_a: PhantomData,
},
Err(e) => return Err(AdaptationFailure::new(self, e)),
};
let uni = match Q::uniform_interface(&mut uniform_builder, env) {
Ok(uni) => uni,
Err(e) => {
return Err(AdaptationFailure::new(
self,
ProgramWarning::Uniform(e).into(),
))
}
};
let warnings = uniform_builder
.warnings
.into_iter()
.map(|w| ProgramError::Warning(w.into()))
.collect();
let program = Program {
repr: self.repr,
uni,
_sem: PhantomData,
_out: PhantomData,
};
Ok(BuiltProgram { program, warnings })
}
pub fn readapt_env<E>(
self,
env: &mut E,
) -> Result<BuiltProgram<B, Sem, Out, Uni>, AdaptationFailure<B, Sem, Out, Uni>>
where
Uni: UniformInterface<B, E>,
{
self.adapt_env(env)
}
}
pub struct ShaderData<B, T>
where
B: ?Sized + ShaderDataBackend<T>,
{
pub(crate) repr: B::ShaderDataRepr,
}
impl<B, T> ShaderData<B, T>
where
B: ?Sized + ShaderDataBackend<T>,
{
pub fn new(
ctx: &mut impl GraphicsContext<Backend = B>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, ShaderDataError> {
let repr = unsafe { ctx.backend().new_shader_data(values.into_iter())? };
Ok(Self { repr })
}
pub fn at(&self, i: usize) -> Result<T, ShaderDataError> {
unsafe { B::get_shader_data_at(&self.repr, i) }
}
pub fn set(&mut self, i: usize, x: T) -> Result<T, ShaderDataError> {
unsafe { B::set_shader_data_at(&mut self.repr, i, x) }
}
pub fn replace(&mut self, values: impl IntoIterator<Item = T>) -> Result<(), ShaderDataError> {
unsafe { B::set_shader_data_values(&mut self.repr, values.into_iter()) }
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ShaderDataError {
CannotCreate,
OutOfBounds {
index: usize,
},
CannotSetData {
index: usize,
},
CannotReplaceData,
}
impl fmt::Display for ShaderDataError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
ShaderDataError::CannotCreate => f.write_str("cannot create shader data"),
ShaderDataError::OutOfBounds { index } => {
write!(f, "cannot get shader data item; out of bounds {}", index)
}
ShaderDataError::CannotSetData { index } => {
write!(f, "cannot get shader data item; out of bounds {}", index)
}
ShaderDataError::CannotReplaceData => f.write_str("cannot replace shader data"),
}
}
}
impl std::error::Error for ShaderDataError {}