use std::cell::RefCell; use glam::Mat3; use smithay::backend::renderer::gles::{ GlesError, GlesFrame, GlesRenderer, GlesTexProgram, Uniform, UniformName, UniformType, UniformValue, }; use super::renderer::NiriRenderer; use super::shader_element::ShaderProgram; pub struct Shaders { pub border: Option, pub shadow: Option, pub clipped_surface: Option, pub resize: Option, pub custom_resize: RefCell>, pub custom_close: RefCell>, pub custom_open: RefCell>, } #[derive(Debug, Clone, Copy)] pub enum ProgramType { Border, Shadow, Resize, Close, Open, } impl Shaders { fn compile(renderer: &mut GlesRenderer) -> Self { let _span = tracy_client::span!("Shaders::compile"); let border = ShaderProgram::compile( renderer, include_str!("border.frag"), &[ UniformName::new("colorspace", UniformType::_1f), UniformName::new("hue_interpolation", UniformType::_1f), UniformName::new("color_from", UniformType::_4f), UniformName::new("color_to", UniformType::_4f), UniformName::new("grad_offset", UniformType::_2f), UniformName::new("grad_width", UniformType::_1f), UniformName::new("grad_vec", UniformType::_2f), UniformName::new("input_to_geo", UniformType::Matrix3x3), UniformName::new("geo_size", UniformType::_2f), UniformName::new("outer_radius", UniformType::_4f), UniformName::new("border_width", UniformType::_1f), ], &[], ) .map_err(|err| { warn!("error compiling border shader: {err:?}"); }) .ok(); let shadow = ShaderProgram::compile( renderer, include_str!("shadow.frag"), &[ UniformName::new("shadow_color", UniformType::_4f), UniformName::new("sigma", UniformType::_1f), UniformName::new("input_to_geo", UniformType::Matrix3x3), UniformName::new("geo_size", UniformType::_2f), UniformName::new("corner_radius", UniformType::_4f), UniformName::new("window_input_to_geo", UniformType::Matrix3x3), UniformName::new("window_geo_size", UniformType::_2f), UniformName::new("window_corner_radius", UniformType::_4f), ], &[], ) .map_err(|err| { warn!("error compiling shadow shader: {err:?}"); }) .ok(); let clipped_surface = renderer .compile_custom_texture_shader( include_str!("clipped_surface.frag"), &[ UniformName::new("niri_scale", UniformType::_1f), UniformName::new("geo_size", UniformType::_2f), UniformName::new("corner_radius", UniformType::_4f), UniformName::new("input_to_geo", UniformType::Matrix3x3), ], ) .map_err(|err| { warn!("error compiling clipped surface shader: {err:?}"); }) .ok(); let resize = compile_resize_program(renderer, include_str!("resize.frag")) .map_err(|err| { warn!("error compiling resize shader: {err:?}"); }) .ok(); Self { border, shadow, clipped_surface, resize, custom_resize: RefCell::new(None), custom_close: RefCell::new(None), custom_open: RefCell::new(None), } } pub fn get_from_frame<'a>(frame: &'a mut GlesFrame<'_>) -> &'a Self { let data = frame.egl_context().user_data(); data.get() .expect("shaders::init() must be called when creating the renderer") } pub fn get(renderer: &mut impl NiriRenderer) -> &Self { let renderer = renderer.as_gles_renderer(); let data = renderer.egl_context().user_data(); data.get() .expect("shaders::init() must be called when creating the renderer") } pub fn replace_custom_resize_program( &self, program: Option, ) -> Option { self.custom_resize.replace(program) } pub fn replace_custom_close_program( &self, program: Option, ) -> Option { self.custom_close.replace(program) } pub fn replace_custom_open_program( &self, program: Option, ) -> Option { self.custom_open.replace(program) } pub fn program(&self, program: ProgramType) -> Option { match program { ProgramType::Border => self.border.clone(), ProgramType::Shadow => self.shadow.clone(), ProgramType::Resize => self .custom_resize .borrow() .clone() .or_else(|| self.resize.clone()), ProgramType::Close => self.custom_close.borrow().clone(), ProgramType::Open => self.custom_open.borrow().clone(), } } } pub fn init(renderer: &mut GlesRenderer) { let shaders = Shaders::compile(renderer); let data = renderer.egl_context().user_data(); if !data.insert_if_missing(|| shaders) { error!("shaders were already compiled"); } } fn compile_resize_program( renderer: &mut GlesRenderer, src: &str, ) -> Result { let mut program = include_str!("resize_prelude.frag").to_string(); program.push_str(src); program.push_str(include_str!("resize_epilogue.frag")); ShaderProgram::compile( renderer, &program, &[ UniformName::new("niri_input_to_curr_geo", UniformType::Matrix3x3), UniformName::new("niri_curr_geo_to_prev_geo", UniformType::Matrix3x3), UniformName::new("niri_curr_geo_to_next_geo", UniformType::Matrix3x3), UniformName::new("niri_curr_geo_size", UniformType::_2f), UniformName::new("niri_geo_to_tex_prev", UniformType::Matrix3x3), UniformName::new("niri_geo_to_tex_next", UniformType::Matrix3x3), UniformName::new("niri_progress", UniformType::_1f), UniformName::new("niri_clamped_progress", UniformType::_1f), UniformName::new("niri_corner_radius", UniformType::_4f), UniformName::new("niri_clip_to_geometry", UniformType::_1f), ], &["niri_tex_prev", "niri_tex_next"], ) } pub fn set_custom_resize_program(renderer: &mut GlesRenderer, src: Option<&str>) { let program = if let Some(src) = src { match compile_resize_program(renderer, src) { Ok(program) => Some(program), Err(err) => { warn!("error compiling custom resize shader: {err:?}"); return; } } } else { None }; if let Some(prev) = Shaders::get(renderer).replace_custom_resize_program(program) { if let Err(err) = prev.destroy(renderer) { warn!("error destroying previous custom resize shader: {err:?}"); } } } fn compile_close_program( renderer: &mut GlesRenderer, src: &str, ) -> Result { let mut program = include_str!("close_prelude.frag").to_string(); program.push_str(src); program.push_str(include_str!("close_epilogue.frag")); ShaderProgram::compile( renderer, &program, &[ UniformName::new("niri_input_to_geo", UniformType::Matrix3x3), UniformName::new("niri_geo_size", UniformType::_2f), UniformName::new("niri_geo_to_tex", UniformType::Matrix3x3), UniformName::new("niri_progress", UniformType::_1f), UniformName::new("niri_clamped_progress", UniformType::_1f), UniformName::new("niri_random_seed", UniformType::_1f), ], &["niri_tex"], ) } pub fn set_custom_close_program(renderer: &mut GlesRenderer, src: Option<&str>) { let program = if let Some(src) = src { match compile_close_program(renderer, src) { Ok(program) => Some(program), Err(err) => { warn!("error compiling custom close shader: {err:?}"); return; } } } else { None }; if let Some(prev) = Shaders::get(renderer).replace_custom_close_program(program) { if let Err(err) = prev.destroy(renderer) { warn!("error destroying previous custom close shader: {err:?}"); } } } fn compile_open_program( renderer: &mut GlesRenderer, src: &str, ) -> Result { let mut program = include_str!("open_prelude.frag").to_string(); program.push_str(src); program.push_str(include_str!("open_epilogue.frag")); ShaderProgram::compile( renderer, &program, &[ UniformName::new("niri_input_to_geo", UniformType::Matrix3x3), UniformName::new("niri_geo_size", UniformType::_2f), UniformName::new("niri_geo_to_tex", UniformType::Matrix3x3), UniformName::new("niri_progress", UniformType::_1f), UniformName::new("niri_clamped_progress", UniformType::_1f), UniformName::new("niri_random_seed", UniformType::_1f), ], &["niri_tex"], ) } pub fn set_custom_open_program(renderer: &mut GlesRenderer, src: Option<&str>) { let program = if let Some(src) = src { match compile_open_program(renderer, src) { Ok(program) => Some(program), Err(err) => { warn!("error compiling custom open shader: {err:?}"); return; } } } else { None }; if let Some(prev) = Shaders::get(renderer).replace_custom_open_program(program) { if let Err(err) = prev.destroy(renderer) { warn!("error destroying previous custom open shader: {err:?}"); } } } pub fn mat3_uniform(name: &str, mat: Mat3) -> Uniform { Uniform::new( name, UniformValue::Matrix3x3 { matrices: vec![mat.to_cols_array()], transpose: false, }, ) }