mod implicit; use std::fmt::Write; pub use implicit::*; // define constants /// No alpha shader define pub const NO_ALPHA: &str = "NO_ALPHA"; /// External texture shader define pub const EXTERNAL: &str = "EXTERNAL"; /// Debug flags shader define pub const DEBUG_FLAGS: &str = "DEBUG_FLAGS"; use super::*; /// Compiles a shader variant. /// /// # Safety /// /// You must call this only when it is safe to compile shaders with GL. pub unsafe fn compile_shader( gl: &ffi::Gles2, variant: ffi::types::GLuint, src: &str, ) -> Result { let shader = gl.CreateShader(variant); if shader == 0 { return Err(GlesError::CreateShaderObject); } gl.ShaderSource( shader, 1, &src.as_ptr() as *const *const u8 as *const *const ffi::types::GLchar, &(src.len() as i32) as *const _, ); gl.CompileShader(shader); let mut status = ffi::FALSE as i32; gl.GetShaderiv(shader, ffi::COMPILE_STATUS, &mut status as *mut _); if status == ffi::FALSE as i32 { let mut max_len = 0; gl.GetShaderiv(shader, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _); let mut error = Vec::with_capacity(max_len as usize); let mut len = 0; gl.GetShaderInfoLog( shader, max_len as _, &mut len as *mut _, error.as_mut_ptr() as *mut _, ); error.set_len(len as usize); error!( "[GL] {}", std::str::from_utf8(&error).unwrap_or("") ); gl.DeleteShader(shader); return Err(GlesError::ShaderCompileError); } Ok(shader) } /// Compiles and links a shader program. /// /// # Safety /// /// You must call this only when it is safe to compile and link shaders with GL. pub unsafe fn link_program( gl: &ffi::Gles2, vert_src: &str, frag_src: &str, ) -> Result { let vert = compile_shader(gl, ffi::VERTEX_SHADER, vert_src)?; let frag = compile_shader(gl, ffi::FRAGMENT_SHADER, frag_src)?; let program = gl.CreateProgram(); gl.AttachShader(program, vert); gl.AttachShader(program, frag); gl.LinkProgram(program); gl.DetachShader(program, vert); gl.DetachShader(program, frag); gl.DeleteShader(vert); gl.DeleteShader(frag); let mut status = ffi::FALSE as i32; gl.GetProgramiv(program, ffi::LINK_STATUS, &mut status as *mut _); if status == ffi::FALSE as i32 { let mut max_len = 0; gl.GetProgramiv(program, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _); let mut error = Vec::with_capacity(max_len as usize); let mut len = 0; gl.GetProgramInfoLog( program, max_len as _, &mut len as *mut _, error.as_mut_ptr() as *mut _, ); error.set_len(len as usize); error!( "[GL] {}", std::str::from_utf8(&error).unwrap_or("") ); gl.DeleteProgram(program); return Err(GlesError::ProgramLinkError); } Ok(program) } pub(super) unsafe fn texture_program( gl: &ffi::Gles2, src: &str, additional_uniforms: &[UniformName<'_>], destruction_callback_sender: Sender, ) -> Result { let create_variant = |defines: &[&str]| -> Result { let shader = src.replace( "//_DEFINES_", &defines.iter().fold(String::new(), |mut shader, define| { let _ = writeln!(&mut shader, "#define {}", define); shader }), ); let debug_shader = src.replace( "//_DEFINES_", &defines .iter() .chain(&[shaders::DEBUG_FLAGS]) .fold(String::new(), |mut shader, define| { let _ = writeln!(shader, "#define {}", define); shader }), ); let program = unsafe { link_program(gl, shaders::VERTEX_SHADER, &shader)? }; let debug_program = unsafe { link_program(gl, shaders::VERTEX_SHADER, debug_shader.as_ref())? }; let vert = c"vert"; let vert_position = c"vert_position"; let tex = c"tex"; let matrix = c"matrix"; let tex_matrix = c"tex_matrix"; let alpha = c"alpha"; let tint = c"tint"; Ok(GlesTexProgramVariant { normal: GlesTexProgramInternal { program, uniform_tex: gl.GetUniformLocation(program, tex.as_ptr() as *const ffi::types::GLchar), uniform_matrix: gl.GetUniformLocation(program, matrix.as_ptr() as *const ffi::types::GLchar), uniform_tex_matrix: gl .GetUniformLocation(program, tex_matrix.as_ptr() as *const ffi::types::GLchar), uniform_alpha: gl.GetUniformLocation(program, alpha.as_ptr() as *const ffi::types::GLchar), attrib_vert: gl.GetAttribLocation(program, vert.as_ptr() as *const ffi::types::GLchar), attrib_vert_position: gl .GetAttribLocation(program, vert_position.as_ptr() as *const ffi::types::GLchar), additional_uniforms: additional_uniforms .iter() .map(|uniform| { let name = CString::new(uniform.name.as_bytes()).expect("Interior null in name"); let location = gl.GetUniformLocation(program, name.as_ptr() as *const ffi::types::GLchar); ( uniform.name.clone().into_owned(), UniformDesc { location, type_: uniform.type_, }, ) }) .collect(), }, debug: GlesTexProgramInternal { program: debug_program, uniform_tex: gl.GetUniformLocation(debug_program, tex.as_ptr() as *const ffi::types::GLchar), uniform_matrix: gl .GetUniformLocation(debug_program, matrix.as_ptr() as *const ffi::types::GLchar), uniform_tex_matrix: gl .GetUniformLocation(debug_program, tex_matrix.as_ptr() as *const ffi::types::GLchar), uniform_alpha: gl .GetUniformLocation(debug_program, alpha.as_ptr() as *const ffi::types::GLchar), attrib_vert: gl.GetAttribLocation(debug_program, vert.as_ptr() as *const ffi::types::GLchar), attrib_vert_position: gl .GetAttribLocation(debug_program, vert_position.as_ptr() as *const ffi::types::GLchar), additional_uniforms: additional_uniforms .iter() .map(|uniform| { let name = CString::new(uniform.name.as_bytes()).expect("Interior null in name"); let location = gl.GetUniformLocation(debug_program, name.as_ptr() as *const ffi::types::GLchar); ( uniform.name.clone().into_owned(), UniformDesc { location, type_: uniform.type_, }, ) }) .collect(), }, // debug flags uniform_tint: gl.GetUniformLocation(debug_program, tint.as_ptr() as *const ffi::types::GLchar), }) }; Ok(GlesTexProgram(Arc::new(GlesTexProgramInner { variants: [ create_variant(&[])?, create_variant(&[shaders::NO_ALPHA])?, create_variant(&[shaders::EXTERNAL])?, ], destruction_callback_sender, }))) } pub(super) unsafe fn solid_program(gl: &ffi::Gles2) -> Result { let program = link_program(gl, shaders::VERTEX_SHADER_SOLID, shaders::FRAGMENT_SHADER_SOLID)?; let matrix = c"matrix"; let color = c"color"; let vert = c"vert"; let position = c"position"; Ok(GlesSolidProgram { program, uniform_matrix: gl.GetUniformLocation(program, matrix.as_ptr() as *const ffi::types::GLchar), uniform_color: gl.GetUniformLocation(program, color.as_ptr() as *const ffi::types::GLchar), attrib_vert: gl.GetAttribLocation(program, vert.as_ptr() as *const ffi::types::GLchar), attrib_position: gl.GetAttribLocation(program, position.as_ptr() as *const ffi::types::GLchar), }) }