diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-05-15 19:38:29 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-05-15 19:38:29 +0400 |
| commit | 5335ef454be4a1318dae36b1795a8578704f5d81 (patch) | |
| tree | a0fd289a1d3eb8662e95634b0cbf6511e4659a58 /src/layout/opening_window.rs | |
| parent | 496cd59df98620e0e861ed9719321d64f3f636c9 (diff) | |
| download | niri-5335ef454be4a1318dae36b1795a8578704f5d81.tar.gz niri-5335ef454be4a1318dae36b1795a8578704f5d81.tar.bz2 niri-5335ef454be4a1318dae36b1795a8578704f5d81.zip | |
Implement custom shader for window-open
Diffstat (limited to 'src/layout/opening_window.rs')
| -rw-r--r-- | src/layout/opening_window.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/layout/opening_window.rs b/src/layout/opening_window.rs new file mode 100644 index 00000000..1a8b3b99 --- /dev/null +++ b/src/layout/opening_window.rs @@ -0,0 +1,156 @@ +use std::collections::HashMap; +use std::time::Duration; + +use anyhow::Context as _; +use glam::{Mat3, Vec2}; +use smithay::backend::allocator::Fourcc; +use smithay::backend::renderer::element::texture::TextureRenderElement; +use smithay::backend::renderer::element::utils::{ + Relocate, RelocateRenderElement, RescaleRenderElement, +}; +use smithay::backend::renderer::element::{Id, Kind, RenderElement}; +use smithay::backend::renderer::gles::{GlesRenderer, Uniform}; +use smithay::backend::renderer::{Renderer as _, Texture}; +use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform}; + +use crate::animation::Animation; +use crate::niri_render_elements; +use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; +use crate::render_helpers::render_to_encompassing_texture; +use crate::render_helpers::shader_element::ShaderRenderElement; +use crate::render_helpers::shaders::{mat3_uniform, ProgramType, Shaders}; + +#[derive(Debug)] +pub struct OpenAnimation { + anim: Animation, + random_seed: f32, +} + +niri_render_elements! { + OpeningWindowRenderElement => { + Texture = RelocateRenderElement<RescaleRenderElement<PrimaryGpuTextureRenderElement>>, + Shader = ShaderRenderElement, + } +} + +impl OpenAnimation { + pub fn new(anim: Animation) -> Self { + Self { + anim, + random_seed: fastrand::f32(), + } + } + + pub fn advance_animations(&mut self, current_time: Duration) { + self.anim.set_current_time(current_time); + } + + pub fn is_done(&self) -> bool { + self.anim.is_done() + } + + // We can't depend on view_rect here, because the result of window opening can be snapshot and + // then rendered elsewhere. + pub fn render( + &self, + renderer: &mut GlesRenderer, + elements: &[impl RenderElement<GlesRenderer>], + geo_size: Size<i32, Logical>, + location: Point<i32, Logical>, + scale: Scale<f64>, + ) -> anyhow::Result<OpeningWindowRenderElement> { + let progress = self.anim.value(); + let clamped_progress = self.anim.clamped_value().clamp(0., 1.); + + let (texture, _sync_point, geo) = render_to_encompassing_texture( + renderer, + scale, + Transform::Normal, + Fourcc::Abgr8888, + elements, + ) + .context("error rendering to texture")?; + + let offset = geo.loc.to_f64().to_logical(scale); + let texture_size = geo.size.to_f64().to_logical(scale); + + if Shaders::get(renderer).program(ProgramType::Open).is_some() { + let mut area = Rectangle::from_loc_and_size(location.to_f64() + offset, texture_size); + + // Expand the area a bit to allow for more varied effects. + let mut target_size = area.size.upscale(1.5); + target_size.w = f64::max(area.size.w + 1000., target_size.w); + target_size.h = f64::max(area.size.h + 1000., target_size.h); + let diff = target_size.to_point() - area.size.to_point(); + area.loc -= diff.downscale(2.); + area.size += diff.to_size(); + + let area = area.to_i32_up(); + let area_loc = Vec2::new(area.loc.x as f32, area.loc.y as f32); + let area_size = Vec2::new(area.size.w as f32, area.size.h as f32); + + let geo_loc = Vec2::new(location.x as f32, location.y as f32); + let geo_size = Vec2::new(geo_size.w as f32, geo_size.h as f32); + + let input_to_geo = Mat3::from_scale(area_size / geo_size) + * Mat3::from_translation((area_loc - geo_loc) / area_size); + + let tex_scale = Vec2::new(scale.x as f32, scale.y as f32); + let tex_loc = Vec2::new(offset.x as f32, offset.y as f32); + let tex_size = Vec2::new(texture.width() as f32, texture.height() as f32) / tex_scale; + + let geo_to_tex = + Mat3::from_translation(-tex_loc / tex_size) * Mat3::from_scale(geo_size / tex_size); + + return Ok(ShaderRenderElement::new( + ProgramType::Open, + area.size, + None, + 1., + vec![ + mat3_uniform("niri_input_to_geo", input_to_geo), + Uniform::new("niri_geo_size", geo_size.to_array()), + mat3_uniform("niri_geo_to_tex", geo_to_tex), + Uniform::new("niri_progress", progress as f32), + Uniform::new("niri_clamped_progress", clamped_progress as f32), + Uniform::new("niri_random_seed", self.random_seed), + ], + HashMap::from([(String::from("niri_tex"), texture.clone())]), + Kind::Unspecified, + ) + .with_location(area.loc) + .into()); + } + + let elem = TextureRenderElement::from_static_texture( + Id::new(), + renderer.id(), + Point::from((0., 0.)), + texture.clone(), + scale.x as i32, + Transform::Normal, + Some(clamped_progress as f32), + None, + None, + None, + Kind::Unspecified, + ); + + let elem = PrimaryGpuTextureRenderElement(elem); + + let center = geo_size.to_point().to_f64().downscale(2.); + let elem = RescaleRenderElement::from_element( + elem, + (center - offset).to_physical_precise_round(scale), + (progress / 2. + 0.5).max(0.), + ); + + let elem = RelocateRenderElement::from_element( + elem, + (location.to_f64() + offset).to_physical_precise_round(scale), + Relocate::Relative, + ); + + Ok(elem.into()) + } +} |
