use glow::{HasContext, NativeUniformLocation}; use super::shader::Shader; use super::snippets::{CodeType, Snippet}; use crate::components::CodeComponent; use crate::graphics::transforms::{viewport::Viewport, Transform}; #[derive(Debug)] pub struct Program { version: &'static str, vertex: Shader, fragment: Shader, geometry: Option, pub native_program: Option<::Program>, } impl Program { pub fn new( vertex: Shader, fragment: Shader, geometry: Option, version: &'static str, ) -> Self { let res = Self { version, vertex, fragment, geometry, native_program: None, }; res } pub fn vertex(&self) -> &Shader { &self.vertex } pub fn fragment(&self) -> &Shader { &self.fragment } pub fn set_hook(&mut self, hook: &str, code: &Snippet) { self.vertex.set_hook(hook, code); self.fragment.set_hook(hook, code); self.geometry.as_mut().map(|g| g.set_hook(hook, code)); } pub fn set_transform(&mut self, value: &T) { self.set_hook("transform", value.snippet()); } pub fn set_viewport(&mut self, viewport: &Viewport) { self.set_hook("viewport", viewport.snippet()); } pub fn compile(&mut self, gl: &glow::Context) -> crate::errors::Result<()> { use crate::errors::Error; self.vertex.define("_GLUMPY__VERTEX_SHADER__"); self.fragment.define("_GLUMPY__FRAGMENT_SHADER__"); unsafe { let program = gl.create_program().map_err(|e| Error::InvalidProgram(e))?; let vertex_shader = self.vertex.compile(&self.version, gl); let fragment_shader = self.fragment.compile(&self.version, gl); gl.attach_shader(program, vertex_shader); gl.attach_shader(program, fragment_shader); // Geom Shader let geom_shader = if let Some(geometry) = self.geometry.as_mut() { geometry.define("_GLUMPY__GEOMETRY_SHADER__"); let geometry_shader = geometry.compile(&self.version, gl); gl.attach_shader(program, geometry_shader); Some(geometry_shader) } else { None }; gl.link_program(program); if !gl.get_program_link_status(program) { return Err(Error::InvalidProgram(gl.get_program_info_log(program))); } gl.detach_shader(program, vertex_shader); gl.detach_shader(program, fragment_shader); if let Some(_) = self.geometry.as_ref() { gl.detach_shader(program, geom_shader.unwrap()); gl.delete_shader(geom_shader.unwrap()); } gl.delete_shader(vertex_shader); gl.delete_shader(fragment_shader); gl.use_program(Some(program)); self.native_program = Some(program); } Ok(()) } pub fn destroy(&self, gl: &glow::Context) { unsafe { self.native_program.map(|p| gl.delete_program(p)); } } pub fn get_uniform_location( &self, gl: &glow::Context, name: &str, ) -> Option { self.native_program .as_ref() .map(|p| unsafe { let location = gl.get_uniform_location(*p, name); location }) .flatten() } } mod test { use super::*; #[test] fn test_program() { let vertex = Shader::new( glow::VERTEX_SHADER, CodeType::<&'static str>::Path("agg-fast-path.vert".into()), ) .unwrap(); let fragment = Shader::new( glow::FRAGMENT_SHADER, CodeType::<&'static str>::Path("agg-fast-path.frag".into()), ) .unwrap(); let program = Program::new(vertex, fragment, None, "330 core"); } }